/* 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
- Free Software Foundation, Inc.
+ Copyright (C) 1993-2016 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
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
+/* Check assumptions made in this file. */
+typedef char static_assert1[sizeof (offsetT) < 8 ? -1 : 1];
+typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
+
#ifdef DEBUG
#define DBG(x) printf x
#else
#define DBG(x)
#endif
-#ifdef OBJ_MAYBE_ELF
+#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; }
/* Fix any of them that we actually care about. */
#undef OUTPUT_FLAVOR
#define OUTPUT_FLAVOR mips_output_flavor()
-#endif
-#if defined (OBJ_ELF)
#include "elf/mips.h"
-#endif
#ifndef ECOFF_DEBUGGING
#define NO_ECOFF_DEBUGGING
#include "ecoff.h"
-#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
static char *mips_regmask_frag;
-#endif
+static char *mips_flags_frag;
#define ZERO 0
#define ATREG 1
#define AT mips_opts.at
-/* Allow override of standard little-endian ECOFF format. */
-
-#ifndef ECOFF_LITTLE_FORMAT
-#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
-#endif
-
extern int target_big_endian;
/* The name of the readonly data section. */
-#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
- ? ".rdata" \
- : OUTPUT_FLAVOR == bfd_target_coff_flavour \
- ? ".rdata" \
- : OUTPUT_FLAVOR == bfd_target_elf_flavour \
- ? ".rodata" \
- : (abort (), ""))
+#define RDATA_SECTION_NAME ".rodata"
/* Ways in which an instruction can be "appended" to the output. */
enum append_method {
/* The opcode's entry in mips_opcodes or mips16_opcodes. */
const struct mips_opcode *insn_mo;
- /* True if this is a mips16 instruction and if we want the extended
- form of INSN_MO. */
- bfd_boolean use_extend;
-
- /* The 16-bit extension instruction to use when USE_EXTEND is true. */
- unsigned short extend;
-
/* The 16-bit or 32-bit bitstring of the instruction itself. This is
- a copy of INSN_MO->match with the operands filled in. */
+ a copy of INSN_MO->match with the operands filled in. If we have
+ decided to use an extended MIPS16 instruction, this includes the
+ extension. */
unsigned long insn_opcode;
/* The frag that contains the instruction. */
/* True if this instruction is complete. */
unsigned int complete_p : 1;
+
+ /* True if this instruction is cleared from history by unconditional
+ branch. */
+ unsigned int cleared_p : 1;
};
/* The ABI to use. */
if it has not been initialized. Changed by `.set mipsN', and the
-mipsN command line option, and the default CPU. */
int isa;
- /* Enabled Application Specific Extensions (ASEs). These are set to -1
- if they have not been initialized. Changed by `.set <asename>', by
- command line options, and based on the default architecture. */
- int ase_mips3d;
- int ase_mdmx;
- int ase_smartmips;
- int ase_dsp;
- int ase_dspr2;
- int ase_mt;
- int ase_mcu;
+ /* Enabled Application Specific Extensions (ASEs). Changed by `.set
+ <asename>', by command line options, and based on the default
+ architecture. */
+ int ase;
/* Whether we are assembling for the mips16 processor. 0 if we are
not, 1 if we are, and -1 if the value has not been initialized.
Changed by `.set mips16' and `.set nomips16', and the -mips16 and
/* Non-zero if we should not autoextend mips16 instructions.
Changed by `.set autoextend' and `.set noautoextend'. */
int noautoextend;
+ /* True if we should only emit 32-bit microMIPS instructions.
+ Changed by `.set insn32' and `.set noinsn32', and the -minsn32
+ and -mno-insn32 command line options. */
+ bfd_boolean insn32;
/* 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;
Changed by .set singlefloat or .set doublefloat, command-line options
-msingle-float or -mdouble-float. The default is false. */
bfd_boolean single_float;
+
+ /* 1 if single-precision operations on odd-numbered registers are
+ allowed. */
+ int oddspreg;
};
-/* 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. */
+/* Specifies whether module level options have been checked yet. */
+static bfd_boolean file_mips_opts_checked = FALSE;
-/* True if -mgp32 was passed. */
-static int file_mips_gp32 = -1;
+/* Do we support nan2008? 0 if we don't, 1 if we do, and -1 if the
+ value has not been initialized. Changed by `.nan legacy' and
+ `.nan 2008', and the -mnan=legacy and -mnan=2008 command line
+ options, and the default CPU. */
+static int mips_nan2008 = -1;
-/* True if -mfp32 was passed. */
-static int file_mips_fp32 = -1;
+/* 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 -msoft-float, 0 if -mhard-float. The default is 0. */
-static int file_mips_soft_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, /* oddspreg */ -1
+};
-/* 1 if -msingle-float, 0 if -mdouble-float. The default is 0. */
-static int file_mips_single_float = 0;
+/* 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_mips3d */ -1, /* ase_mdmx */ -1,
- /* ase_smartmips */ 0, /* ase_dsp */ -1, /* ase_dspr2 */ -1, /* ase_mt */ -1,
- /* ase_mcu */ -1, /* mips16 */ -1, /* micromips */ -1, /* noreorder */ 0,
- /* at */ ATREG, /* warn_about_macros */ 0, /* nomove */ 0, /* nobopt */ 0,
- /* noautoextend */ 0, /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN,
- /* sym32 */ FALSE, /* soft_float */ FALSE, /* single_float */ FALSE
+ /* 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, /* oddspreg */ -1
};
+/* Which bits of file_ase were explicitly set or cleared by ASE options. */
+static unsigned int file_ase_explicit;
+
/* These variables are filled in with the masks of registers used.
The object format code reads them and puts them in the appropriate
place. */
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;
|| ((EXPR)->X_op == O_symbol && (EXPR)->X_add_number == 0))
#endif
-/* True if -mips3d was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_mips3d;
-
-/* True if -mdmx was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_mdmx;
-
-/* True if -msmartmips was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_smartmips;
-
-#define ISA_SUPPORTS_SMARTMIPS (mips_opts.isa == ISA_MIPS32 \
- || mips_opts.isa == ISA_MIPS32R2)
-
-/* True if -mdsp was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_dsp;
-
-#define ISA_SUPPORTS_DSP_ASE (mips_opts.isa == ISA_MIPS32R2 \
- || mips_opts.isa == ISA_MIPS64R2)
-
-#define ISA_SUPPORTS_DSP64_ASE (mips_opts.isa == ISA_MIPS64R2)
-
-/* True if -mdspr2 was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_dspr2;
-
-#define ISA_SUPPORTS_DSPR2_ASE (mips_opts.isa == ISA_MIPS32R2 \
- || mips_opts.isa == ISA_MIPS64R2)
-
-/* True if -mmt was passed or implied by arguments passed on the
- command line (e.g., by -march). */
-static int file_ase_mt;
-
-#define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2 \
- || mips_opts.isa == ISA_MIPS64R2)
-
-#define ISA_SUPPORTS_MCU_ASE (mips_opts.isa == ISA_MIPS32R2 \
- || mips_opts.isa == ISA_MIPS64R2 \
- || mips_opts.micromips)
-
/* 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
/* Likewise 64-bit registers. */
#define ABI_NEEDS_64BIT_REGS(ABI) \
- ((ABI) == N32_ABI \
+ ((ABI) == N32_ABI \
|| (ABI) == N64_ABI \
|| (ABI) == O64_ABI)
+#define ISA_IS_R6(ISA) \
+ ((ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64R6)
+
/* Return true if ISA supports 64 bit wide gp registers. */
#define ISA_HAS_64BIT_REGS(ISA) \
((ISA) == ISA_MIPS3 \
|| (ISA) == ISA_MIPS4 \
|| (ISA) == ISA_MIPS5 \
|| (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
/* 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_MIPS32R6 \
|| (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
/* 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 \
+ || (ISA) == ISA_MIPS64R6 \
|| (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_MIPS32R6 \
|| (ISA) == ISA_MIPS64R2 \
- || mips_opts.ase_smartmips \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6 \
+ || (mips_opts.ase & ASE_SMARTMIPS) \
|| mips_opts.micromips \
)
/* Return true if ISA supports single-precision floats in odd registers. */
-#define ISA_HAS_ODD_SINGLE_FPR(ISA) \
- ((ISA) == ISA_MIPS32 \
- || (ISA) == ISA_MIPS32R2 \
- || (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+#define ISA_HAS_ODD_SINGLE_FPR(ISA, CPU)\
+ (((ISA) == ISA_MIPS32 \
+ || (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6 \
+ || (CPU) == CPU_R5900) \
+ && (CPU) != CPU_LOONGSON_3A)
/* 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_MIPS32R6 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
+
+/* Return true if ISA supports legacy NAN. */
+#define ISA_HAS_LEGACY_NAN(ISA) \
+ ((ISA) == ISA_MIPS1 \
+ || (ISA) == ISA_MIPS2 \
+ || (ISA) == ISA_MIPS3 \
+ || (ISA) == ISA_MIPS4 \
+ || (ISA) == ISA_MIPS5 \
+ || (ISA) == ISA_MIPS32 \
+ || (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS64 \
+ || (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
/* True if CPU has a ror instruction. */
#define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU)
+/* True if CPU is in the Octeon family */
+#define CPU_IS_OCTEON(CPU) ((CPU) == CPU_OCTEON || (CPU) == CPU_OCTEONP \
+ || (CPU) == CPU_OCTEON2 || (CPU) == CPU_OCTEON3)
+
/* True if CPU has seq/sne and seqi/snei instructions. */
-#define CPU_HAS_SEQ(CPU) ((CPU) == CPU_OCTEON)
+#define CPU_HAS_SEQ(CPU) (CPU_IS_OCTEON (CPU))
-/* True if CPU does not implement the all the coprocessor insns. For these
- CPUs only those COP insns are accepted that are explicitly marked to be
- available on the CPU. ISA membership for COP insns is ignored. */
-#define NO_ISA_COP(CPU) ((CPU) == CPU_OCTEON)
+/* True, if CPU has support for ldc1 and sdc1. */
+#define CPU_HAS_LDC1_SDC1(CPU) \
+ ((mips_opts.isa != ISA_MIPS1) && ((CPU) != CPU_R5900))
/* True if mflo and mfhi can be immediately followed by instructions
which write to the HI and LO registers.
#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_MIPS32R6 \
|| mips_opts.isa == ISA_MIPS64 \
|| mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.isa == ISA_MIPS64R3 \
+ || mips_opts.isa == ISA_MIPS64R5 \
+ || mips_opts.isa == ISA_MIPS64R6 \
|| mips_opts.arch == CPU_R4010 \
+ || mips_opts.arch == CPU_R5900 \
|| mips_opts.arch == CPU_R10000 \
|| mips_opts.arch == CPU_R12000 \
|| mips_opts.arch == CPU_R14000 \
/* 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.arch == CPU_R3900 \
+ || mips_opts.arch == CPU_R5900 \
|| mips_opts.micromips \
)
/* Whether the processor uses hardware interlocks to avoid delays
required by coprocessor instructions, and thus does not require
nops to be inserted. This applies to instructions marked
- INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
- between instructions marked INSN_WRITE_COND_CODE and ones marked
+ INSN_LOAD_COPROC, INSN_COPROC_MOVE, and to delays between
+ instructions marked INSN_WRITE_COND_CODE and ones marked
INSN_READ_COND_CODE. These nops are only required at MIPS ISA
levels I, II, and III and microMIPS mode instructions are always
interlocked. */
#define MF_HILO_INSN(PINFO) \
((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
-/* Returns true for a (non floating-point) coprocessor instruction. Reading
- or writing the condition code is only possible on the coprocessors and
- these insns are not marked with INSN_COP. Thus for these insns use the
- condition-code flags. */
-#define COP_INSN(PINFO) \
- (PINFO != INSN_MACRO \
- && ((PINFO) & (FP_S | FP_D)) == 0 \
- && ((PINFO) & (INSN_COP | INSN_READ_COND_CODE | INSN_WRITE_COND_CODE)))
-
/* Whether code compression (either of the MIPS16 or the microMIPS ASEs)
has been selected. This implies, in particular, that addresses of text
labels have their LSB set. */
#define HAVE_CODE_COMPRESSION \
((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 << (GPR_SIZE - 1)) - 1))
+#define GPR_SMIN (-GPR_SMAX - 1)
+
/* MIPS PIC level. */
enum mips_pic_level mips_pic;
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 struct mips_cl_insn micromips_nop32_insn;
/* The appropriate nop for the current mode. */
-#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn \
- : (mips_opts.micromips ? µmips_nop16_insn : &nop_insn))
+#define NOP_INSN (mips_opts.mips16 \
+ ? &mips16_nop_insn \
+ : (mips_opts.micromips \
+ ? (mips_opts.insn32 \
+ ? µmips_nop32_insn \
+ : µmips_nop16_insn) \
+ : &nop_insn))
/* The size of NOP_INSN in bytes. */
-#define NOP_INSN_SIZE (HAVE_CODE_COMPRESSION ? 2 : 4)
+#define NOP_INSN_SIZE ((mips_opts.mips16 \
+ || (mips_opts.micromips && !mips_opts.insn32)) \
+ ? 2 : 4)
/* If this is set, it points to a frag holding nop instructions which
were inserted before the start of a noreorder section. If those
/* The number of instructions we've seen since prev_nop_frag. */
static int prev_nop_frag_since;
-/* For ECOFF and ELF, relocations against symbols are done in two
- parts, with a HI relocation and a LO relocation. Each relocation
- has only 16 bits of space to store an addend. This means that in
- order for the linker to handle carries correctly, it must be able
- to locate both the HI and the LO relocation. This means that the
- relocations must appear in order in the relocation table.
+/* Relocations against symbols are sometimes done in two parts, with a HI
+ relocation and a LO relocation. Each relocation has only 16 bits of
+ space to store an addend. This means that in order for the linker to
+ handle carries correctly, it must be able to locate both the HI and
+ the LO relocation. This means that the relocations must appear in
+ order in the relocation table.
In order to implement this, we keep track of each unmatched HI
relocation. We then sort them so that they immediately precede the
static fragS *prev_reloc_op_frag;
-/* Map normal MIPS register numbers to mips16 register numbers. */
-
-#define X ILLEGAL_REG
-static const int mips32_to_16_reg_map[] =
-{
- X, X, 2, 3, 4, 5, 6, 7,
- X, X, X, X, X, X, X, X,
- 0, 1, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X
-};
-#undef X
-
/* Map mips16 register numbers to normal MIPS register numbers. */
static const unsigned int mips16_to_32_reg_map[] =
16, 17, 2, 3, 4, 5, 6, 7
};
-/* Map normal MIPS register numbers to microMIPS register numbers. */
-
-#define mips32_to_micromips_reg_b_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_c_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_d_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_e_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_f_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_g_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_l_map mips32_to_16_reg_map
-
-#define X ILLEGAL_REG
-/* reg type h: 4, 5, 6. */
-static const int mips32_to_micromips_reg_h_map[] =
-{
- X, X, X, X, 4, 5, 6, X,
- X, X, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-/* reg type m: 0, 17, 2, 3, 16, 18, 19, 20. */
-static const int mips32_to_micromips_reg_m_map[] =
-{
- 0, X, 2, 3, X, X, X, X,
- X, X, X, X, X, X, X, X,
- 4, 1, 5, 6, 7, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-/* reg type q: 0, 2-7. 17. */
-static const int mips32_to_micromips_reg_q_map[] =
-{
- 0, X, 2, 3, 4, 5, 6, 7,
- X, X, X, X, X, X, X, X,
- X, 1, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-#define mips32_to_micromips_reg_n_map mips32_to_micromips_reg_m_map
-#undef X
-
/* Map microMIPS register numbers to normal MIPS register numbers. */
-#define micromips_to_32_reg_b_map mips16_to_32_reg_map
-#define micromips_to_32_reg_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_map[] =
+static const unsigned int micromips_to_32_reg_h_map1[] =
{
5, 5, 6, 4, 4, 4, 4, 4
};
-
-/* The microMIPS registers with type i. */
-static const unsigned int micromips_to_32_reg_i_map[] =
+static const unsigned int micromips_to_32_reg_h_map2[] =
{
6, 7, 7, 21, 22, 5, 6, 7
};
-#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;
/* Branch without likely bit. If label is out of range, we turn:
- beq reg1, reg2, label
+ beq reg1, reg2, label
delay slot
into
The information we store for this type of relaxation is the argument
code found in the opcode file for this relocation, the register
- selected as the assembler temporary, whether the branch is
- unconditional, whether it is compact, whether it stores the link
- address implicitly in $ra, whether relaxation of out-of-range 32-bit
- branches to a sequence of instructions is enabled, and whether the
- displacement of a branch is too large to fit as an immediate argument
- of a 16-bit and a 32-bit branch, respectively. */
-#define RELAX_MICROMIPS_ENCODE(type, at, uncond, compact, link, \
+ selected as the assembler temporary, whether in the 32-bit
+ instruction mode, whether the branch is unconditional, whether it is
+ compact, whether there is no delay-slot instruction available to fill
+ in, whether it stores the link address implicitly in $ra, whether
+ relaxation of out-of-range 32-bit branches to a sequence of
+ instructions is enabled, and whether the displacement of a branch is
+ too large to fit as an immediate argument of a 16-bit and a 32-bit
+ branch, respectively. */
+#define RELAX_MICROMIPS_ENCODE(type, at, insn32, \
+ uncond, compact, link, nods, \
relax32, toofar16, toofar32) \
(0x40000000 \
| ((type) & 0xff) \
| (((at) & 0x1f) << 8) \
- | ((uncond) ? 0x2000 : 0) \
- | ((compact) ? 0x4000 : 0) \
- | ((link) ? 0x8000 : 0) \
- | ((relax32) ? 0x10000 : 0) \
- | ((toofar16) ? 0x20000 : 0) \
- | ((toofar32) ? 0x40000 : 0))
+ | ((insn32) ? 0x2000 : 0) \
+ | ((uncond) ? 0x4000 : 0) \
+ | ((compact) ? 0x8000 : 0) \
+ | ((link) ? 0x10000 : 0) \
+ | ((nods) ? 0x20000 : 0) \
+ | ((relax32) ? 0x40000 : 0) \
+ | ((toofar16) ? 0x80000 : 0) \
+ | ((toofar32) ? 0x100000 : 0))
#define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
#define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
#define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
-#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x2000) != 0)
-#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x4000) != 0)
-#define RELAX_MICROMIPS_LINK(i) (((i) & 0x8000) != 0)
-#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x10000) != 0)
-
-#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x20000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x20000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x20000)
-#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x40000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x40000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x40000)
+#define RELAX_MICROMIPS_INSN32(i) (((i) & 0x2000) != 0)
+#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x4000) != 0)
+#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x8000) != 0)
+#define RELAX_MICROMIPS_LINK(i) (((i) & 0x10000) != 0)
+#define RELAX_MICROMIPS_NODS(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x40000) != 0)
+
+#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x80000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x80000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x80000)
+#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x100000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x100000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x100000)
+
+/* Sign-extend 16-bit value X. */
+#define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
/* Is the given value a sign-extended 32-bit value? */
#define IS_SEXT_32BIT_NUM(x) \
#define IS_SEXT_12BIT_NUM(x) \
(((((x) & 0xfff) ^ 0x800LL) - 0x800LL) == (x))
+/* Is the given value a sign-extended 9-bit value? */
+#define IS_SEXT_9BIT_NUM(x) \
+ (((((x) & 0x1ff) ^ 0x100LL) - 0x100LL) == (x))
+
/* Is the given value a zero-extended 32-bit value? Or a negated one? */
#define IS_ZEXT_32BIT_NUM(x) \
(((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) \
EXTRACT_BITS ((INSN).insn_opcode, \
MIPS16OP_MASK_##FIELD, \
MIPS16OP_SH_##FIELD)
+
+/* The MIPS16 EXTEND opcode, shifted left 16 places. */
+#define MIPS16_EXTEND (0xf000U << 16)
\f
/* Whether or not we are emitting a branch-likely macro. */
static bfd_boolean emit_branch_likely_macro = FALSE;
\f
/* Prototypes for static functions. */
-#define internalError() \
- as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
-
enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
static void append_insn
static void load_register (int, expressionS *, int);
static void macro_start (void);
static void macro_end (void);
-static void macro (struct mips_cl_insn * ip);
+static void macro (struct mips_cl_insn *ip, char *str);
static void mips16_macro (struct mips_cl_insn * ip);
static void mips_ip (char *str, struct mips_cl_insn * ip);
static void mips16_ip (char *str, struct mips_cl_insn * ip);
static void mips16_immed
- (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean,
- unsigned long *, bfd_boolean *, unsigned short *);
+ (const char *, unsigned int, int, bfd_reloc_code_real_type, offsetT,
+ unsigned int, unsigned long *);
static size_t my_getSmallExpression
(expressionS *, bfd_reloc_code_real_type *, char *);
static void my_getExpression (expressionS *, char *);
static void s_cpreturn (int);
static void s_dtprelword (int);
static void s_dtpreldword (int);
+static void s_tprelword (int);
+static void s_tpreldword (int);
static void s_gpvalue (int);
static void s_gpword (int);
static void s_gpdword (int);
+static void s_ehword (int);
static void s_cpadd (int);
static void s_insn (int);
-static void md_obj_begin (void);
-static void md_obj_end (void);
+static void s_nan (int);
+static void s_module (int);
static void s_mips_ent (int);
static void s_mips_end (int);
static void s_mips_frame (int);
static void s_mips_loc (int);
static bfd_boolean pic_need_relax (symbolS *, asection *);
static int relaxed_branch_length (fragS *, asection *, int);
-static int validate_mips_insn (const struct mips_opcode *);
-static int validate_micromips_insn (const struct mips_opcode *);
static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
+static void file_mips_check_options (void);
/* Table and functions used to map between CPU/ISA names, and
ISA levels, and CPU numbers. */
struct mips_cpu_info
{
const char *name; /* CPU or ISA name. */
- int flags; /* ASEs available, or ISA flag. */
+ int flags; /* MIPS_CPU_* flags. */
+ int ase; /* Set of ASEs implemented by the CPU. */
int isa; /* ISA level. */
int cpu; /* CPU number (default CPU if ISA). */
};
#define MIPS_CPU_IS_ISA 0x0001 /* Is this an ISA? (If 0, a CPU.) */
-#define MIPS_CPU_ASE_SMARTMIPS 0x0002 /* CPU implements SmartMIPS ASE */
-#define MIPS_CPU_ASE_DSP 0x0004 /* CPU implements DSP ASE */
-#define MIPS_CPU_ASE_MT 0x0008 /* CPU implements MT ASE */
-#define MIPS_CPU_ASE_MIPS3D 0x0010 /* CPU implements MIPS-3D ASE */
-#define MIPS_CPU_ASE_MDMX 0x0020 /* CPU implements MDMX ASE */
-#define MIPS_CPU_ASE_DSPR2 0x0040 /* CPU implements DSP R2 ASE */
-#define MIPS_CPU_ASE_MCU 0x0080 /* CPU implements MCU ASE */
static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
\f
-/* Pseudo-op table.
-
- The following pseudo-ops from the Kane and Heinrich MIPS book
- should be defined here, but are currently unsupported: .alias,
- .galive, .gjaldef, .gjrlive, .livereg, .noalias.
-
- The following pseudo-ops from the Kane and Heinrich MIPS book are
- specific to the type of debugging information being generated, and
- should be defined by the object format: .aent, .begin, .bend,
- .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
- .vreg.
+/* Command-line options. */
+const char *md_shortopts = "O::g::G:";
- The following pseudo-ops from the Kane and Heinrich MIPS book are
- not MIPS CPU specific, but are also not specific to the object file
- format. This file is probably the best place to define them, but
- they are not currently supported: .asm0, .endr, .lab, .struct. */
+enum options
+ {
+ OPTION_MARCH = OPTION_MD_BASE,
+ OPTION_MTUNE,
+ OPTION_MIPS1,
+ OPTION_MIPS2,
+ OPTION_MIPS3,
+ OPTION_MIPS4,
+ OPTION_MIPS5,
+ OPTION_MIPS32,
+ OPTION_MIPS64,
+ OPTION_MIPS32R2,
+ OPTION_MIPS32R3,
+ OPTION_MIPS32R5,
+ OPTION_MIPS32R6,
+ OPTION_MIPS64R2,
+ OPTION_MIPS64R3,
+ OPTION_MIPS64R5,
+ OPTION_MIPS64R6,
+ OPTION_MIPS16,
+ OPTION_NO_MIPS16,
+ OPTION_MIPS3D,
+ OPTION_NO_MIPS3D,
+ OPTION_MDMX,
+ OPTION_NO_MDMX,
+ OPTION_DSP,
+ OPTION_NO_DSP,
+ OPTION_MT,
+ OPTION_NO_MT,
+ OPTION_VIRT,
+ OPTION_NO_VIRT,
+ OPTION_MSA,
+ OPTION_NO_MSA,
+ OPTION_SMARTMIPS,
+ OPTION_NO_SMARTMIPS,
+ OPTION_DSPR2,
+ OPTION_NO_DSPR2,
+ OPTION_DSPR3,
+ OPTION_NO_DSPR3,
+ OPTION_EVA,
+ OPTION_NO_EVA,
+ OPTION_XPA,
+ OPTION_NO_XPA,
+ OPTION_MICROMIPS,
+ OPTION_NO_MICROMIPS,
+ OPTION_MCU,
+ OPTION_NO_MCU,
+ OPTION_COMPAT_ARCH_BASE,
+ OPTION_M4650,
+ OPTION_NO_M4650,
+ OPTION_M4010,
+ OPTION_NO_M4010,
+ OPTION_M4100,
+ OPTION_NO_M4100,
+ OPTION_M3900,
+ OPTION_NO_M3900,
+ OPTION_M7000_HILO_FIX,
+ 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,
+ OPTION_NO_FIX_LOONGSON2F_NOP,
+ OPTION_FIX_VR4120,
+ OPTION_NO_FIX_VR4120,
+ OPTION_FIX_VR4130,
+ OPTION_NO_FIX_VR4130,
+ OPTION_FIX_CN63XXP1,
+ OPTION_NO_FIX_CN63XXP1,
+ OPTION_TRAP,
+ OPTION_BREAK,
+ OPTION_EB,
+ OPTION_EL,
+ OPTION_FP32,
+ OPTION_GP32,
+ OPTION_CONSTRUCT_FLOATS,
+ OPTION_NO_CONSTRUCT_FLOATS,
+ OPTION_FP64,
+ OPTION_FPXX,
+ OPTION_GP64,
+ OPTION_RELAX_BRANCH,
+ OPTION_NO_RELAX_BRANCH,
+ OPTION_INSN32,
+ OPTION_NO_INSN32,
+ OPTION_MSHARED,
+ OPTION_MNO_SHARED,
+ OPTION_MSYM32,
+ OPTION_MNO_SYM32,
+ OPTION_SOFT_FLOAT,
+ OPTION_HARD_FLOAT,
+ OPTION_SINGLE_FLOAT,
+ OPTION_DOUBLE_FLOAT,
+ OPTION_32,
+ OPTION_CALL_SHARED,
+ OPTION_CALL_NONPIC,
+ OPTION_NON_SHARED,
+ OPTION_XGOT,
+ OPTION_MABI,
+ OPTION_N32,
+ OPTION_64,
+ OPTION_MDEBUG,
+ OPTION_NO_MDEBUG,
+ OPTION_PDR,
+ OPTION_NO_PDR,
+ OPTION_MVXWORKS_PIC,
+ OPTION_NAN,
+ OPTION_ODD_SPREG,
+ OPTION_NO_ODD_SPREG,
+ OPTION_END_OF_ENUM
+ };
-static const pseudo_typeS mips_pseudo_table[] =
+struct option md_longopts[] =
{
- /* MIPS specific pseudo-ops. */
- {"option", s_option, 0},
- {"set", s_mipsset, 0},
- {"rdata", s_change_sec, 'r'},
- {"sdata", s_change_sec, 's'},
- {"livereg", s_ignore, 0},
- {"abicalls", s_abicalls, 0},
- {"cpload", s_cpload, 0},
- {"cpsetup", s_cpsetup, 0},
- {"cplocal", s_cplocal, 0},
- {"cprestore", s_cprestore, 0},
- {"cpreturn", s_cpreturn, 0},
- {"dtprelword", s_dtprelword, 0},
- {"dtpreldword", s_dtpreldword, 0},
- {"gpvalue", s_gpvalue, 0},
- {"gpword", s_gpword, 0},
- {"gpdword", s_gpdword, 0},
- {"cpadd", s_cpadd, 0},
- {"insn", s_insn, 0},
-
- /* Relatively generic pseudo-ops that happen to be used on MIPS
- chips. */
- {"asciiz", stringer, 8 + 1},
- {"bss", s_change_sec, 'b'},
- {"err", s_err, 0},
- {"half", s_cons, 1},
- {"dword", s_cons, 3},
- {"weakext", s_mips_weakext, 0},
- {"origin", s_org, 0},
- {"repeat", s_rept, 0},
+ /* Options which specify architecture. */
+ {"march", required_argument, NULL, OPTION_MARCH},
+ {"mtune", required_argument, NULL, OPTION_MTUNE},
+ {"mips0", no_argument, NULL, OPTION_MIPS1},
+ {"mips1", no_argument, NULL, OPTION_MIPS1},
+ {"mips2", no_argument, NULL, OPTION_MIPS2},
+ {"mips3", no_argument, NULL, OPTION_MIPS3},
+ {"mips4", no_argument, NULL, OPTION_MIPS4},
+ {"mips5", no_argument, NULL, OPTION_MIPS5},
+ {"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},
+ {"mips32r6", no_argument, NULL, OPTION_MIPS32R6},
+ {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
+ {"mips64r3", no_argument, NULL, OPTION_MIPS64R3},
+ {"mips64r5", no_argument, NULL, OPTION_MIPS64R5},
+ {"mips64r6", no_argument, NULL, OPTION_MIPS64R6},
- /* For MIPS this is non-standard, but we define it for consistency. */
- {"sbss", s_change_sec, 'B'},
+ /* Options which specify Application Specific Extensions (ASEs). */
+ {"mips16", no_argument, NULL, OPTION_MIPS16},
+ {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+ {"mips3d", no_argument, NULL, OPTION_MIPS3D},
+ {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
+ {"mdmx", no_argument, NULL, OPTION_MDMX},
+ {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX},
+ {"mdsp", no_argument, NULL, OPTION_DSP},
+ {"mno-dsp", no_argument, NULL, OPTION_NO_DSP},
+ {"mmt", no_argument, NULL, OPTION_MT},
+ {"mno-mt", no_argument, NULL, OPTION_NO_MT},
+ {"msmartmips", no_argument, NULL, OPTION_SMARTMIPS},
+ {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
+ {"mdspr2", no_argument, NULL, OPTION_DSPR2},
+ {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
+ {"mdspr3", no_argument, NULL, OPTION_DSPR3},
+ {"mno-dspr3", no_argument, NULL, OPTION_NO_DSPR3},
+ {"meva", no_argument, NULL, OPTION_EVA},
+ {"mno-eva", no_argument, NULL, OPTION_NO_EVA},
+ {"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
+ {"mno-micromips", no_argument, NULL, OPTION_NO_MICROMIPS},
+ {"mmcu", no_argument, NULL, OPTION_MCU},
+ {"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},
- /* These pseudo-ops are defined in read.c, but must be overridden
+ /* Old-style architecture options. Don't add more of these. */
+ {"m4650", no_argument, NULL, OPTION_M4650},
+ {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+ {"m4010", no_argument, NULL, OPTION_M4010},
+ {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+ {"m4100", no_argument, NULL, OPTION_M4100},
+ {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+ {"m3900", no_argument, NULL, OPTION_M3900},
+ {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
+
+ /* Options which enable bug fixes. */
+ {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
+ {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mfix-loongson2f-jump", no_argument, NULL, OPTION_FIX_LOONGSON2F_JUMP},
+ {"mno-fix-loongson2f-jump", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_JUMP},
+ {"mfix-loongson2f-nop", no_argument, NULL, OPTION_FIX_LOONGSON2F_NOP},
+ {"mno-fix-loongson2f-nop", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_NOP},
+ {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120},
+ {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
+ {"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130},
+ {"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},
+
+ /* Miscellaneous options. */
+ {"trap", no_argument, NULL, OPTION_TRAP},
+ {"no-break", no_argument, NULL, OPTION_TRAP},
+ {"break", no_argument, NULL, OPTION_BREAK},
+ {"no-trap", no_argument, NULL, OPTION_BREAK},
+ {"EB", no_argument, NULL, OPTION_EB},
+ {"EL", no_argument, NULL, OPTION_EL},
+ {"mfp32", no_argument, NULL, OPTION_FP32},
+ {"mgp32", no_argument, NULL, OPTION_GP32},
+ {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
+ {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
+ {"mfp64", no_argument, NULL, OPTION_FP64},
+ {"mfpxx", no_argument, NULL, OPTION_FPXX},
+ {"mgp64", no_argument, NULL, OPTION_GP64},
+ {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
+ {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+ {"minsn32", no_argument, NULL, OPTION_INSN32},
+ {"mno-insn32", no_argument, NULL, OPTION_NO_INSN32},
+ {"mshared", no_argument, NULL, OPTION_MSHARED},
+ {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED},
+ {"msym32", no_argument, NULL, OPTION_MSYM32},
+ {"mno-sym32", no_argument, NULL, OPTION_MNO_SYM32},
+ {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT},
+ {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT},
+ {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT},
+ {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT},
+ {"modd-spreg", no_argument, NULL, OPTION_ODD_SPREG},
+ {"mno-odd-spreg", no_argument, NULL, OPTION_NO_ODD_SPREG},
+
+ /* Strictly speaking this next option is ELF specific,
+ but we allow it for other ports as well in order to
+ make testing easier. */
+ {"32", no_argument, NULL, OPTION_32},
+
+ /* ELF-specific options. */
+ {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+ {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+ {"call_nonpic", no_argument, NULL, OPTION_CALL_NONPIC},
+ {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+ {"xgot", no_argument, NULL, OPTION_XGOT},
+ {"mabi", required_argument, NULL, OPTION_MABI},
+ {"n32", no_argument, NULL, OPTION_N32},
+ {"64", no_argument, NULL, OPTION_64},
+ {"mdebug", no_argument, NULL, OPTION_MDEBUG},
+ {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
+ {"mpdr", no_argument, NULL, OPTION_PDR},
+ {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
+ {"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC},
+ {"mnan", required_argument, NULL, OPTION_NAN},
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+\f
+/* Information about either an Application Specific Extension or an
+ optional architecture feature that, for simplicity, we treat in the
+ same way as an ASE. */
+struct mips_ase
+{
+ /* The name of the ASE, used in both the command-line and .set options. */
+ const char *name;
+
+ /* The associated ASE_* flags. If the ASE is available on both 32-bit
+ and 64-bit architectures, the flags here refer to the subset that
+ is available on both. */
+ unsigned int flags;
+
+ /* The ASE_* flag used for instructions that are available on 64-bit
+ architectures but that are not included in FLAGS. */
+ unsigned int flags64;
+
+ /* The command-line options that turn the ASE on and off. */
+ int option_on;
+ int option_off;
+
+ /* The minimum required architecture revisions for MIPS32, MIPS64,
+ microMIPS32 and microMIPS64, or -1 if the extension isn't supported. */
+ int mips32_rev;
+ int mips64_rev;
+ int micromips32_rev;
+ int micromips64_rev;
+
+ /* The architecture where the ASE was removed or -1 if the extension has not
+ been removed. */
+ int rem_rev;
+};
+
+/* A table of all supported ASEs. */
+static const struct mips_ase mips_ases[] = {
+ { "dsp", ASE_DSP, ASE_DSP64,
+ OPTION_DSP, OPTION_NO_DSP,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "dspr2", ASE_DSP | ASE_DSPR2, 0,
+ OPTION_DSPR2, OPTION_NO_DSPR2,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "dspr3", ASE_DSP | ASE_DSPR2 | ASE_DSPR3, 0,
+ OPTION_DSPR3, OPTION_NO_DSPR3,
+ 6, 6, -1, -1,
+ -1 },
+
+ { "eva", ASE_EVA, 0,
+ OPTION_EVA, OPTION_NO_EVA,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "mcu", ASE_MCU, 0,
+ OPTION_MCU, OPTION_NO_MCU,
+ 2, 2, 2, 2,
+ -1 },
+
+ /* Deprecated in MIPS64r5, but we don't implement that yet. */
+ { "mdmx", ASE_MDMX, 0,
+ OPTION_MDMX, OPTION_NO_MDMX,
+ -1, 1, -1, -1,
+ 6 },
+
+ /* Requires 64-bit FPRs, so the minimum MIPS32 revision is 2. */
+ { "mips3d", ASE_MIPS3D, 0,
+ OPTION_MIPS3D, OPTION_NO_MIPS3D,
+ 2, 1, -1, -1,
+ 6 },
+
+ { "mt", ASE_MT, 0,
+ OPTION_MT, OPTION_NO_MT,
+ 2, 2, -1, -1,
+ -1 },
+
+ { "smartmips", ASE_SMARTMIPS, 0,
+ OPTION_SMARTMIPS, OPTION_NO_SMARTMIPS,
+ 1, -1, -1, -1,
+ 6 },
+
+ { "virt", ASE_VIRT, ASE_VIRT64,
+ OPTION_VIRT, OPTION_NO_VIRT,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "msa", ASE_MSA, ASE_MSA64,
+ OPTION_MSA, OPTION_NO_MSA,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "xpa", ASE_XPA, 0,
+ OPTION_XPA, OPTION_NO_XPA,
+ 2, 2, -1, -1,
+ -1 },
+};
+
+/* The set of ASEs that require -mfp64. */
+#define FP64_ASES (ASE_MIPS3D | ASE_MDMX | ASE_MSA)
+
+/* Groups of ASE_* flags that represent different revisions of an ASE. */
+static const unsigned int mips_ase_groups[] = {
+ ASE_DSP | ASE_DSPR2 | ASE_DSPR3
+};
+\f
+/* Pseudo-op table.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .alias,
+ .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ specific to the type of debugging information being generated, and
+ should be defined by the object format: .aent, .begin, .bend,
+ .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+ .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not specific to the object file
+ format. This file is probably the best place to define them, but
+ they are not currently supported: .asm0, .endr, .lab, .struct. */
+
+static const pseudo_typeS mips_pseudo_table[] =
+{
+ /* MIPS specific pseudo-ops. */
+ {"option", s_option, 0},
+ {"set", s_mipsset, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"sdata", s_change_sec, 's'},
+ {"livereg", s_ignore, 0},
+ {"abicalls", s_abicalls, 0},
+ {"cpload", s_cpload, 0},
+ {"cpsetup", s_cpsetup, 0},
+ {"cplocal", s_cplocal, 0},
+ {"cprestore", s_cprestore, 0},
+ {"cpreturn", s_cpreturn, 0},
+ {"dtprelword", s_dtprelword, 0},
+ {"dtpreldword", s_dtpreldword, 0},
+ {"tprelword", s_tprelword, 0},
+ {"tpreldword", s_tpreldword, 0},
+ {"gpvalue", s_gpvalue, 0},
+ {"gpword", s_gpword, 0},
+ {"gpdword", s_gpdword, 0},
+ {"ehword", s_ehword, 0},
+ {"cpadd", s_cpadd, 0},
+ {"insn", s_insn, 0},
+ {"nan", s_nan, 0},
+ {"module", s_module, 0},
+
+ /* Relatively generic pseudo-ops that happen to be used on MIPS
+ chips. */
+ {"asciiz", stringer, 8 + 1},
+ {"bss", s_change_sec, 'b'},
+ {"err", s_err, 0},
+ {"half", s_cons, 1},
+ {"dword", s_cons, 3},
+ {"weakext", s_mips_weakext, 0},
+ {"origin", s_org, 0},
+ {"repeat", s_rept, 0},
+
+ /* For MIPS this is non-standard, but we define it for consistency. */
+ {"sbss", s_change_sec, 'B'},
+
+ /* These pseudo-ops are defined in read.c, but must be overridden
here for one reason or another. */
{"align", s_align, 0},
{"byte", s_cons, 0},
{"section", s_change_section, 0},
{"short", s_cons, 1},
{"single", s_float_cons, 'f'},
+ {"stabd", s_mips_stab, 'd'},
{"stabn", s_mips_stab, 'n'},
+ {"stabs", s_mips_stab, 's'},
{"text", s_change_sec, 't'},
{"word", s_cons, 2},
int
mips_address_bytes (void)
{
+ file_mips_check_options ();
return HAVE_64BIT_ADDRESSES ? 8 : 4;
}
static inline void
mips_clear_insn_labels (void)
{
- register struct insn_label_list **pl;
+ struct insn_label_list **pl;
segment_info_type *si;
if (now_seg)
{
for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
;
-
+
si = seg_info (now_seg);
*pl = si->label_list;
si->label_list = NULL;
\f
static char *expr_end;
-/* Expressions which appear in instructions. These are set by
- mips_ip. */
+/* 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;
-static expressionS offset_expr;
-/* Relocs associated with imm_expr and offset_expr. */
+/* The relocatable field in an instruction and the relocs associated
+ with it. These variables are used for instructions like LUI and
+ JAL as well as true offsets. They are also used for address
+ operands in macros. */
-static bfd_reloc_code_real_type imm_reloc[3]
- = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static expressionS offset_expr;
static bfd_reloc_code_real_type offset_reloc[3]
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
static unsigned int forced_insn_length;
-#ifdef OBJ_ELF
+/* True if we are assembling an instruction. All dot symbols defined during
+ this time should be treated as code labels. */
+
+static bfd_boolean mips_assembling_insn;
+
/* The pdr segment for per procedure frame/regmask info. Not used for
ECOFF debugging. */
static segT pdr_seg;
-#endif
/* The default target format to use. */
{
switch (OUTPUT_FLAVOR)
{
- case bfd_target_ecoff_flavour:
- return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
- case bfd_target_coff_flavour:
- return "pe-mips";
case bfd_target_elf_flavour:
#ifdef TE_VXWORKS
if (!HAVE_64BIT_OBJECTS && !HAVE_NEWABI)
}
}
+/* Return the ISA revision that is currently in use, or 0 if we are
+ generating code for MIPS V or below. */
+
+static int
+mips_isa_rev (void)
+{
+ 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;
+
+ if (mips_opts.isa == ISA_MIPS32R6 || mips_opts.isa == ISA_MIPS64R6)
+ return 6;
+
+ /* microMIPS implies revision 2 or above. */
+ if (mips_opts.micromips)
+ return 2;
+
+ if (mips_opts.isa == ISA_MIPS32 || mips_opts.isa == ISA_MIPS64)
+ return 1;
+
+ return 0;
+}
+
+/* Return the mask of all ASEs that are revisions of those in FLAGS. */
+
+static unsigned int
+mips_ase_mask (unsigned int flags)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ase_groups); i++)
+ if (flags & mips_ase_groups[i])
+ flags |= mips_ase_groups[i];
+ return flags;
+}
+
+/* Check whether the current ISA supports ASE. Issue a warning if
+ appropriate. */
+
+static void
+mips_check_isa_supports_ase (const struct mips_ase *ase)
+{
+ const char *base;
+ int min_rev, size;
+ static unsigned int warned_isa;
+ static unsigned int warned_fp32;
+
+ if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+ min_rev = mips_opts.micromips ? ase->micromips64_rev : ase->mips64_rev;
+ else
+ min_rev = mips_opts.micromips ? ase->micromips32_rev : ase->mips32_rev;
+ if ((min_rev < 0 || mips_isa_rev () < min_rev)
+ && (warned_isa & ase->flags) != ase->flags)
+ {
+ warned_isa |= ase->flags;
+ 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"
+ " `%s' extension"), size, base, ase->name);
+ else
+ as_warn (_("the `%s' extension requires %s%d revision %d or greater"),
+ ase->name, base, size, min_rev);
+ }
+ else if ((ase->rem_rev > 0 && mips_isa_rev () >= ase->rem_rev)
+ && (warned_isa & ase->flags) != ase->flags)
+ {
+ warned_isa |= ase->flags;
+ base = mips_opts.micromips ? "microMIPS" : "MIPS";
+ size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
+ as_warn (_("the `%s' extension was removed in %s%d revision %d"),
+ ase->name, base, size, ase->rem_rev);
+ }
+
+ if ((ase->flags & FP64_ASES)
+ && 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);
+ }
+}
+
+/* Check all enabled ASEs to see whether they are supported by the
+ chosen architecture. */
+
+static void
+mips_check_isa_supports_ases (void)
+{
+ unsigned int i, mask;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ {
+ mask = mips_ase_mask (mips_ases[i].flags);
+ if ((mips_opts.ase & mask) == mips_ases[i].flags)
+ mips_check_isa_supports_ase (&mips_ases[i]);
+ }
+}
+
+/* Set the state of ASE to ENABLED_P. Return the mask of ASE_* flags
+ that were affected. */
+
+static unsigned int
+mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts,
+ bfd_boolean enabled_p)
+{
+ unsigned int mask;
+
+ mask = mips_ase_mask (ase->flags);
+ opts->ase &= ~mask;
+ if (enabled_p)
+ opts->ase |= ase->flags;
+ return mask;
+}
+
+/* Return the ASE called NAME, or null if none. */
+
+static const struct mips_ase *
+mips_lookup_ase (const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if (strcmp (name, mips_ases[i].name) == 0)
+ return &mips_ases[i];
+ return NULL;
+}
+
/* Return the length of a microMIPS instruction in bytes. If bits of
- the mask beyond the low 16 are 0, then it is a 16-bit instruction.
- Otherwise assume a 32-bit instruction; 48-bit instructions (0x1f
- major opcode) will require further modifications to the opcode
- table. */
+ the mask beyond the low 16 are 0, then it is a 16-bit instruction,
+ otherwise it is a 32-bit instruction. */
static inline unsigned int
micromips_insn_length (const struct mips_opcode *mo)
{
- return (mo->mask >> 16) == 0 ? 2 : 4;
+ return mips_opcode_32bit_p (mo) ? 4 : 2;
+}
+
+/* Return the length of MIPS16 instruction OPCODE. */
+
+static inline unsigned int
+mips16_opcode_length (unsigned long opcode)
+{
+ return (opcode >> 16) == 0 ? 2 : 4;
}
/* Return the length of instruction INSN. */
if (mips_opts.micromips)
return micromips_insn_length (insn->insn_mo);
else if (mips_opts.mips16)
- return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2;
+ return mips16_opcode_length (insn->insn_opcode);
else
return 4;
}
size_t i;
insn->insn_mo = mo;
- insn->use_extend = FALSE;
- insn->extend = 0;
insn->insn_opcode = mo->match;
insn->frag = NULL;
insn->where = 0;
insn->noreorder_p = (mips_opts.noreorder > 0);
insn->mips16_absolute_jump_p = 0;
insn->complete_p = 0;
+ 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_insert_operand (struct mips_cl_insn *insn,
+ const struct mips_operand *operand, unsigned int uval)
+{
+ insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
+}
+
+/* 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. */
si->tc_segment_info_data.micromips = mips_opts.micromips;
}
-/* Install INSN at the location specified by its "frag" and "where" fields. */
+/* Read a standard MIPS instruction from BUF. */
-static void
-install_insn (const struct mips_cl_insn *insn)
+static unsigned long
+read_insn (char *buf)
{
- char *f = insn->frag->fr_literal + insn->where;
- if (!HAVE_CODE_COMPRESSION)
- md_number_to_chars (f, insn->insn_opcode, 4);
- else if (mips_opts.micromips)
- {
- unsigned int length = insn_length (insn);
- if (length == 2)
- md_number_to_chars (f, insn->insn_opcode, 2);
- else if (length == 4)
- {
- md_number_to_chars (f, insn->insn_opcode >> 16, 2);
- f += 2;
- md_number_to_chars (f, insn->insn_opcode & 0xffff, 2);
- }
- else
- as_bad (_("48-bit microMIPS instructions are not supported"));
- }
- else if (insn->mips16_absolute_jump_p)
- {
- md_number_to_chars (f, insn->insn_opcode >> 16, 2);
- md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2);
- }
+ if (target_big_endian)
+ return bfd_getb32 ((bfd_byte *) buf);
else
- {
- if (insn->use_extend)
- {
- md_number_to_chars (f, 0xf000 | insn->extend, 2);
- f += 2;
- }
- md_number_to_chars (f, insn->insn_opcode, 2);
- }
- mips_record_compressed_mode ();
+ return bfd_getl32 ((bfd_byte *) buf);
}
-/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
- and install the opcode in the new location. */
+/* Write standard MIPS instruction INSN to BUF. Return a pointer to
+ the next byte. */
-static void
-move_insn (struct mips_cl_insn *insn, fragS *frag, long where)
+static char *
+write_insn (char *buf, unsigned int insn)
{
- size_t i;
+ md_number_to_chars (buf, insn, 4);
+ return buf + 4;
+}
- insn->frag = frag;
- insn->where = where;
- for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+/* Read a microMIPS or MIPS16 opcode from BUF, given that it
+ has length LENGTH. */
+
+static unsigned long
+read_compressed_insn (char *buf, unsigned int length)
+{
+ unsigned long insn;
+ unsigned int i;
+
+ insn = 0;
+ for (i = 0; i < length; i += 2)
+ {
+ insn <<= 16;
+ if (target_big_endian)
+ insn |= bfd_getb16 ((char *) buf);
+ else
+ insn |= bfd_getl16 ((char *) buf);
+ buf += 2;
+ }
+ return insn;
+}
+
+/* Write microMIPS or MIPS16 instruction INSN to BUF, given that the
+ instruction is LENGTH bytes long. Return a pointer to the next byte. */
+
+static char *
+write_compressed_insn (char *buf, unsigned int insn, unsigned int length)
+{
+ unsigned int i;
+
+ for (i = 0; i < length; i += 2)
+ md_number_to_chars (buf + i, insn >> ((length - i - 2) * 8), 2);
+ return buf + length;
+}
+
+/* Install INSN at the location specified by its "frag" and "where" fields. */
+
+static void
+install_insn (const struct mips_cl_insn *insn)
+{
+ char *f = insn->frag->fr_literal + insn->where;
+ if (HAVE_CODE_COMPRESSION)
+ write_compressed_insn (f, insn->insn_opcode, insn_length (insn));
+ else
+ write_insn (f, insn->insn_opcode);
+ mips_record_compressed_mode ();
+}
+
+/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
+ and install the opcode in the new location. */
+
+static void
+move_insn (struct mips_cl_insn *insn, fragS *frag, long where)
+{
+ size_t i;
+
+ insn->frag = frag;
+ insn->where = where;
+ for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
if (insn->fixp[i] != NULL)
{
insn->fixp[i]->fx_frag = frag;
}
}
+/* 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 = concat (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;
+ }
+
+ free ((char *) msg);
+}
+
/* 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}, \
{"$28", RTYPE_NUM | 28}, \
{"$29", RTYPE_NUM | 29}, \
{"$30", RTYPE_NUM | 30}, \
- {"$31", RTYPE_NUM | 31}
+ {"$31", RTYPE_NUM | 31}
#define FPU_REGISTER_NAMES \
{"$f0", RTYPE_FPU | 0}, \
{"$ta0", RTYPE_GP | 12}, /* alias for $t4 */ \
{"$ta1", RTYPE_GP | 13}, /* alias for $t5 */ \
{"$ta2", RTYPE_GP | 14}, /* alias for $t6 */ \
- {"$ta3", RTYPE_GP | 15} /* alias for $t7 */
+ {"$ta3", RTYPE_GP | 15} /* alias for $t7 */
/* Remaining symbolic register names */
#define SYMBOLIC_REGISTER_NAMES \
{"$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}, \
/* The $txx registers depends on the abi,
these will be added later into the symbol table from
- one of the tables below once mips_abi is set after
+ one of the tables below once mips_abi is set after
parsing of arguments from the command line. */
SYMBOLIC_REGISTER_NAMES,
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';
+ channels = 0;
+ if (!mips_parse_register_1 (s, e, &symval))
+ {
+ if (!channels_ptr)
+ return FALSE;
+
+ /* 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;
+ }
+
+ if (channels == 0
+ || !mips_parse_register_1 (s, m, &symval)
+ || (symval & (RTYPE_VI | RTYPE_VF | RTYPE_R5900_ACC)) == 0)
+ return FALSE;
+ }
+
+ *sptr = e;
+ *symval_ptr = symval;
+ if (channels_ptr)
+ *channels_ptr = channels;
+ return TRUE;
+}
+
+/* Check if SPTR points at a valid register specifier according to TYPES.
+ If so, then return 1, advance S to consume the specifier and store
+ the register's number in REGNOP, otherwise return 0. */
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+ unsigned int regno;
- /* Look for a register symbol. */
- if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section)
+ if (mips_parse_register (s, ®no, NULL))
{
- int r = S_GET_VALUE (symbolP);
- if (r & types)
- reg = r & RNUM_MASK;
- else if ((types & RTYPE_VEC) && (r & ~1) == (RTYPE_GP | 2))
- /* Convert GP reg $v0/1 to MDMX reg $v0/1! */
- reg = (r & RNUM_MASK) - 2;
+ if (types & RTYPE_VEC)
+ regno = mips_prefer_vec_regno (regno);
+ if (regno & types)
+ regno &= RNUM_MASK;
+ else
+ regno = ~0;
}
- /* Else see if this is a register defined in an itbl entry. */
- else if ((types & RTYPE_GP) && itbl_have_entries)
+ else
{
- char *n = *s;
- unsigned long r;
-
- if (*n == '$')
- ++n;
- if (itbl_get_reg_val (n, &r))
- reg = r & RNUM_MASK;
+ if (types & RWARN)
+ as_warn (_("unrecognized register name `%s'"), *s);
+ regno = ~0;
}
+ if (regnop)
+ *regnop = regno;
+ return regno <= RNUM_MASK;
+}
- /* 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);
+/* 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. */
- *e = save_c;
- if (regnop)
- *regnop = reg;
- return reg >= 0;
+static char *
+mips_parse_vu0_channels (char *s, unsigned int *channels)
+{
+ unsigned int i;
+
+ *channels = 0;
+ for (i = 0; i < 4; i++)
+ if (*s == "xyzw"[i])
+ {
+ *channels |= 1 << (3 - i);
+ ++s;
+ }
+ return s;
}
-/* Check if S points at a valid register list according to TYPES.
- If so, then return 1, advance S to consume the list and store
- the registers present on the list as a bitmask of ones in REGLISTP,
- otherwise return 0. A valid list comprises a comma-separated
- enumeration of valid single registers and/or dash-separated
- contiguous register ranges as determined by their numbers.
+/* Token types for parsed operand lists. */
+enum mips_operand_token_type {
+ /* A plain register, e.g. $f2. */
+ OT_REG,
- As a special exception if one of s0-s7 registers is specified as
- the range's lower delimiter and s8 (fp) is its upper one, then no
- registers whose numbers place them between s7 and s8 (i.e. $24-$29)
- are selected; they have to be listed separately if needed. */
+ /* A 4-bit XYZW channel mask. */
+ OT_CHANNELS,
-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;
- unsigned int regno;
+ /* A constant vector index, e.g. [1]. */
+ OT_INTEGER_INDEX,
+
+ /* A register vector index, e.g. [$2]. */
+ OT_REG_INDEX,
+
+ /* A continuous range of registers, e.g. $s0-$s4. */
+ OT_REG_RANGE,
+
+ /* A (possibly relocated) expression. */
+ OT_INTEGER,
+
+ /* A floating-point value. */
+ OT_FLOAT,
+
+ /* A single character. This can be '(', ')' or ',', but '(' only appears
+ before OT_REGs. */
+ OT_CHAR,
+
+ /* A doubled character, either "--" or "++". */
+ OT_DOUBLE_CHAR,
+
+ /* The end of the operand list. */
+ OT_END
+};
+
+/* A parsed operand token. */
+struct mips_operand_token
+{
+ /* The type of token. */
+ enum mips_operand_token_type type;
+ union
+ {
+ /* The register symbol value for an OT_REG 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));
+}
+
+/* Check whether S is '(' followed by a register name. Add OT_CHAR
+ and OT_REG tokens for them if so, and return a pointer to the first
+ unconsumed character. Return null otherwise. */
+
+static char *
+mips_parse_base_start (char *s)
+{
+ struct mips_operand_token token;
+ unsigned int regno, 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;
+ const char *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;
+ }
- while (reg_lookup (s, types, ®no))
+ /* Handle tokens that start with a register. */
+ if (mips_parse_register (&s, ®no1, &channels))
{
- lastregno = regno;
- if (**s == '-')
+ if (channels)
{
- (*s)++;
- ok = reg_lookup (s, types, &lastregno);
- if (ok && lastregno < regno)
- ok = FALSE;
- if (!ok)
- break;
+ /* 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;
}
- if (lastregno == FP && regno >= S0 && regno <= S7)
+ SKIP_SPACE_TABS (s);
+ if (*s == '-')
{
- lastregno = S7;
- reglist |= 1 << FP;
+ /* 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;
}
- regmask = 1 << lastregno;
- regmask = (regmask << 1) - 1;
- regmask ^= (1 << regno) - 1;
- reglist |= regmask;
- s_endlist = *s;
- if (**s != ',')
- break;
- (*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 (ok)
- *s = s_endlist;
- else
- *s = s_reset;
- if (reglistp)
- *reglistp = reglist;
- return ok && reglist != 0;
+ if (float_format)
+ {
+ /* First try to treat expressions as floats. */
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (float_format, (char *) token.u.flt.data,
+ &token.u.flt.length);
+ end = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err && *err)
+ {
+ 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 and
- architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
+/* 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 (mips_opts.ase_mdmx)
- isa |= INSN_MDMX;
- if (mips_opts.ase_dsp)
- isa |= INSN_DSP;
- if (mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE)
- isa |= INSN_DSP64;
- if (mips_opts.ase_dspr2)
- isa |= INSN_DSPR2;
- if (mips_opts.ase_mt)
- isa |= INSN_MT;
- if (mips_opts.ase_mips3d)
- isa |= INSN_MIPS3D;
- if (mips_opts.ase_smartmips)
- isa |= INSN_SMARTMIPS;
- if (mips_opts.ase_mcu)
- isa |= INSN_MCU;
-
- /* Don't accept instructions based on the ISA if the CPU does not implement
- all the coprocessor insns. */
- if (NO_ISA_COP (mips_opts.arch)
- && COP_INSN (mo->pinfo))
- isa = 0;
-
- if (!OPCODE_IS_MEMBER (mo, isa, mips_opts.arch))
+ if (ISA_HAS_64BIT_REGS (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
static bfd_boolean
is_opcode_valid_16 (const struct mips_opcode *mo)
{
- return OPCODE_IS_MEMBER (mo, mips_opts.isa, mips_opts.arch) ? TRUE : FALSE;
+ 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. */
+ explicitly requested. Always TRUE in the standard MIPS mode.
+ Use is_size_valid_16 for MIPS16 opcodes. */
static bfd_boolean
is_size_valid (const struct mips_opcode *mo)
if (!mips_opts.micromips)
return TRUE;
+ if (mips_opts.insn32)
+ {
+ if (mo->pinfo != INSN_MACRO && micromips_insn_length (mo) != 4)
+ return FALSE;
+ if ((mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0)
+ return FALSE;
+ }
if (!forced_insn_length)
return TRUE;
if (mo->pinfo == INSN_MACRO)
return forced_insn_length == micromips_insn_length (mo);
}
+/* Return TRUE if the size of the MIPS16 opcode MO matches one
+ explicitly requested. */
+
+static bfd_boolean
+is_size_valid_16 (const struct mips_opcode *mo)
+{
+ if (!forced_insn_length)
+ return TRUE;
+ if (mo->pinfo == INSN_MACRO)
+ return FALSE;
+ if (forced_insn_length == 2 && mips_opcode_32bit_p (mo))
+ return FALSE;
+ if (forced_insn_length == 4 && (mo->pinfo2 & INSN2_SHORT_ONLY))
+ return FALSE;
+ return TRUE;
+}
+
/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
- of the preceding instruction. Always TRUE in the standard MIPS mode. */
+ of the preceding instruction. Always TRUE in the standard MIPS mode.
+
+ We don't accept macros in 16-bit delay slots to avoid a case where
+ a macro expansion fails because it relies on a preceding 32-bit real
+ instruction to have matched and does not handle the operands correctly.
+ The only macros that may expand to 16-bit instructions are JAL that
+ cannot be placed in a delay slot anyway, and corner cases of BALIGN
+ and BGT (that likewise cannot be placed in a delay slot) that decay to
+ a NOP. In all these cases the macros precede any corresponding real
+ instruction definitions in the opcode table, so they will match in the
+ second pass where the size of the delay slot is ignored and therefore
+ produce correct code. */
static bfd_boolean
is_delay_slot_valid (const struct mips_opcode *mo)
return TRUE;
if (mo->pinfo == INSN_MACRO)
- return TRUE;
+ return (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) == 0;
if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
&& micromips_insn_length (mo) != 4)
return FALSE;
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. 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 *),
+ struct mips_operand_array *operands)
+{
+ const char *s;
+ unsigned long used_bits, doubled, undefined, opno, mask;
+ const struct mips_operand *operand;
+
+ 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 ',':
+ case '(':
+ case ')':
+ break;
+
+ case '#':
+ s++;
+ break;
+
+ default:
+ if (!decode_operand)
+ operand = decode_mips16_operand (*s, mips_opcode_32bit_p (opcode));
+ 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;
+ }
+ 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 (decode_operand && (*s == '+' || *s == 'm' || *s == '-'))
+ ++s;
+ opno += 1;
+ break;
+ }
+ 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 |= mask;
+ undefined = ~used_bits & insn_bits;
+ if (opcode->pinfo != INSN_MACRO && undefined)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
+ undefined, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits &= ~insn_bits;
+ if (used_bits)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"),
+ used_bits, opcode->name, opcode->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* The MIPS16 version of validate_mips_insn. */
+
+static int
+validate_mips16_insn (const struct mips_opcode *opcode,
+ struct mips_operand_array *operands)
+{
+ unsigned long insn_bits = mips_opcode_32bit_p (opcode) ? 0xffffffff : 0xffff;
+
+ return validate_mips_insn (opcode, insn_bits, 0, operands);
+}
+
+/* The microMIPS version of validate_mips_insn. */
+
+static int
+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): "
+ "%s %s"), length, opc->name, opc->args);
+ return 0;
+ }
+ major = opc->match >> (10 + 8 * (length - 2));
+ if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
+ || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
+ {
+ as_bad (_("internal error: bad microMIPS opcode "
+ "(opcode/length mismatch): %s %s"), opc->name, opc->args);
+ return 0;
+ }
+
+ /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
+ insn_bits = 1 << 4 * length;
+ insn_bits <<= 4 * length;
+ insn_bits -= 1;
+ return validate_mips_insn (opc, insn_bits, decode_micromips_operand,
+ operands);
+}
+
/* This function is called once, at assembler startup time. It should set up
all the tables, etc. that the MD part of the assembler will need. */
as_bad (_("-G may not be used in position-independent code"));
g_switch_value = 0;
}
+ else if (mips_abicalls)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_bad (_("-G may not be used with abicalls"));
+ 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]))
- 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. */
- for (i = 0; reg_names[i].name; i++)
+ for (i = 0; reg_names[i].name; i++)
symbol_table_insert (symbol_new (reg_names[i].name, reg_section,
reg_names[i].num, /* & RNUM_MASK, */
&zero_address_frag));
if (HAVE_NEWABI)
- for (i = 0; reg_names_n32n64[i].name; i++)
+ for (i = 0; reg_names_n32n64[i].name; i++)
symbol_table_insert (symbol_new (reg_names_n32n64[i].name, reg_section,
reg_names_n32n64[i].num, /* & RNUM_MASK, */
&zero_address_frag));
else
- for (i = 0; reg_names_o32[i].name; i++)
+ for (i = 0; reg_names_o32[i].name; i++)
symbol_table_insert (symbol_new (reg_names_o32[i].name, reg_section,
reg_names_o32[i].num, /* & RNUM_MASK, */
&zero_address_frag));
+ for (i = 0; i < 32; i++)
+ {
+ char regname[6];
+
+ /* R5900 VU0 floating-point register. */
+ sprintf (regname, "$vf%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VF | i, &zero_address_frag));
+
+ /* R5900 VU0 integer register. */
+ sprintf (regname, "$vi%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VI | i, &zero_address_frag));
+
+ /* MSA register. */
+ sprintf (regname, "$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_set_gp_size (stdoutput, g_switch_value);
-#ifdef OBJ_ELF
- if (IS_ELF)
+ /* On a native system other than VxWorks, sections must be aligned
+ to 16 byte boundaries. When configured for an embedded ELF
+ target, we don't bother. */
+ if (strncmp (TARGET_OS, "elf", 3) != 0
+ && strncmp (TARGET_OS, "vxworks", 7) != 0)
{
- /* On a native system other than VxWorks, sections must be aligned
- to 16 byte boundaries. When configured for an embedded ELF
- target, we don't bother. */
- if (strncmp (TARGET_OS, "elf", 3) != 0
- && strncmp (TARGET_OS, "vxworks", 7) != 0)
- {
- (void) bfd_set_section_alignment (stdoutput, text_section, 4);
- (void) bfd_set_section_alignment (stdoutput, data_section, 4);
- (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
- }
+ (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+ }
- /* Create a .reginfo section for register masks and a .mdebug
- section for debugging information. */
+ /* Create a .reginfo section for register masks and a .mdebug
+ section for debugging information. */
+ {
+ segT seg;
+ subsegT subseg;
+ flagword flags;
+ segT sec;
+
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The ABI says this section should be loaded so that the
+ running program can access it. However, we don't load it
+ if we are configured for an embedded target */
+ flags = SEC_READONLY | SEC_DATA;
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ flags |= SEC_ALLOC | SEC_LOAD;
+
+ if (mips_abi != N64_ABI)
{
- segT seg;
- subsegT subseg;
- flagword flags;
- segT sec;
-
- seg = now_seg;
- subseg = now_subseg;
-
- /* The ABI says this section should be loaded so that the
- running program can access it. However, we don't load it
- if we are configured for an embedded target */
- flags = SEC_READONLY | SEC_DATA;
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- flags |= SEC_ALLOC | SEC_LOAD;
-
- if (mips_abi != N64_ABI)
- {
- sec = subseg_new (".reginfo", (subsegT) 0);
+ sec = subseg_new (".reginfo", (subsegT) 0);
- bfd_set_section_flags (stdoutput, sec, flags);
- bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
- mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
- }
- else
- {
- /* The 64-bit ABI uses a .MIPS.options section rather than
- .reginfo section. */
- sec = subseg_new (".MIPS.options", (subsegT) 0);
- bfd_set_section_flags (stdoutput, sec, flags);
- bfd_set_section_alignment (stdoutput, sec, 3);
+ mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+ }
+ else
+ {
+ /* The 64-bit ABI uses a .MIPS.options section rather than
+ .reginfo section. */
+ sec = subseg_new (".MIPS.options", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, 3);
- /* Set up the option header. */
- {
- Elf_Internal_Options opthdr;
- char *f;
-
- opthdr.kind = ODK_REGINFO;
- opthdr.size = (sizeof (Elf_External_Options)
- + sizeof (Elf64_External_RegInfo));
- opthdr.section = 0;
- opthdr.info = 0;
- f = frag_more (sizeof (Elf_External_Options));
- bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
- (Elf_External_Options *) f);
-
- mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
- }
- }
+ /* Set up the option header. */
+ {
+ Elf_Internal_Options opthdr;
+ char *f;
+
+ opthdr.kind = ODK_REGINFO;
+ opthdr.size = (sizeof (Elf_External_Options)
+ + sizeof (Elf64_External_RegInfo));
+ opthdr.section = 0;
+ opthdr.info = 0;
+ f = frag_more (sizeof (Elf_External_Options));
+ bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+ (Elf_External_Options *) f);
+
+ mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+ }
+ }
- if (ECOFF_DEBUGGING)
- {
- sec = subseg_new (".mdebug", (subsegT) 0);
- (void) bfd_set_section_flags (stdoutput, sec,
- SEC_HAS_CONTENTS | SEC_READONLY);
- (void) bfd_set_section_alignment (stdoutput, sec, 2);
- }
- else if (mips_flag_pdr)
- {
- pdr_seg = subseg_new (".pdr", (subsegT) 0);
- (void) bfd_set_section_flags (stdoutput, pdr_seg,
- SEC_READONLY | SEC_RELOC
- | SEC_DEBUGGING);
- (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
- }
+ sec = subseg_new (".MIPS.abiflags", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec,
+ SEC_READONLY | SEC_DATA | SEC_ALLOC | SEC_LOAD);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+ mips_flags_frag = frag_more (sizeof (Elf_External_ABIFlags_v0));
- subseg_set (seg, subseg);
+ if (ECOFF_DEBUGGING)
+ {
+ sec = subseg_new (".mdebug", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, sec,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+ (void) bfd_set_section_alignment (stdoutput, sec, 2);
+ }
+ else if (mips_flag_pdr)
+ {
+ pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, pdr_seg,
+ SEC_READONLY | SEC_RELOC
+ | SEC_DEBUGGING);
+ (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
}
- }
-#endif /* OBJ_ELF */
- if (! ECOFF_DEBUGGING)
- md_obj_begin ();
+ subseg_set (seg, subseg);
+ }
if (mips_fix_vr4120)
init_vr4120_conflicts ();
}
-void
-md_mips_end (void)
+static inline void
+fpabi_incompatible_with (int fpabi, const char *what)
{
- mips_emit_delays ();
- if (! ECOFF_DEBUGGING)
- md_obj_end ();
+ as_warn (_(".gnu_attribute %d,%d is incompatible with `%s'"),
+ Tag_GNU_MIPS_ABI_FP, fpabi, what);
+}
+
+static inline void
+fpabi_requires (int fpabi, const char *what)
+{
+ as_warn (_(".gnu_attribute %d,%d requires `%s'"),
+ Tag_GNU_MIPS_ABI_FP, fpabi, what);
+}
+
+/* Check -mabi and register sizes against the specified FP ABI. */
+static void
+check_fpabi (int fpabi)
+{
+ switch (fpabi)
+ {
+ case Val_GNU_MIPS_ABI_FP_DOUBLE:
+ if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ if (file_mips_opts.gp == 64 && file_mips_opts.fp == 32)
+ fpabi_incompatible_with (fpabi, "gp=64 fp=32");
+ else if (file_mips_opts.gp == 32 && file_mips_opts.fp == 64)
+ fpabi_incompatible_with (fpabi, "gp=32 fp=64");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_XX:
+ if (mips_abi != O32_ABI)
+ fpabi_requires (fpabi, "-mabi=32");
+ else if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ else if (file_mips_opts.fp != 0)
+ fpabi_requires (fpabi, "fp=xx");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_64A:
+ case Val_GNU_MIPS_ABI_FP_64:
+ if (mips_abi != O32_ABI)
+ fpabi_requires (fpabi, "-mabi=32");
+ else if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ else if (file_mips_opts.fp != 64)
+ fpabi_requires (fpabi, "fp=64");
+ else if (fpabi == Val_GNU_MIPS_ABI_FP_64 && !file_mips_opts.oddspreg)
+ fpabi_incompatible_with (fpabi, "nooddspreg");
+ else if (fpabi == Val_GNU_MIPS_ABI_FP_64A && file_mips_opts.oddspreg)
+ fpabi_requires (fpabi, "nooddspreg");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_SINGLE:
+ if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (!file_mips_opts.single_float)
+ fpabi_requires (fpabi, "singlefloat");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_SOFT:
+ if (!file_mips_opts.soft_float)
+ fpabi_requires (fpabi, "softfloat");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_OLD_64:
+ as_warn (_(".gnu_attribute %d,%d is no longer supported"),
+ Tag_GNU_MIPS_ABI_FP, fpabi);
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_NAN2008:
+ /* Silently ignore compatibility value. */
+ break;
+
+ default:
+ as_warn (_(".gnu_attribute %d,%d is not a recognized"
+ " floating-point ABI"), Tag_GNU_MIPS_ABI_FP, fpabi);
+ break;
+ }
+}
+
+/* Perform consistency checks on the current options. */
+
+static void
+mips_check_options (struct mips_set_options *opts, bfd_boolean abi_checks)
+{
+ /* Check the size of integer registers agrees with the ABI and ISA. */
+ if (opts->gp == 64 && !ISA_HAS_64BIT_REGS (opts->isa))
+ as_bad (_("`gp=64' used with a 32-bit processor"));
+ else if (abi_checks
+ && opts->gp == 32 && ABI_NEEDS_64BIT_REGS (mips_abi))
+ as_bad (_("`gp=32' used with a 64-bit ABI"));
+ else if (abi_checks
+ && opts->gp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi))
+ as_bad (_("`gp=64' used with a 32-bit ABI"));
+
+ /* Check the size of the float registers agrees with the ABI and ISA. */
+ switch (opts->fp)
+ {
+ case 0:
+ if (!CPU_HAS_LDC1_SDC1 (opts->arch))
+ as_bad (_("`fp=xx' used with a cpu lacking ldc1/sdc1 instructions"));
+ else if (opts->single_float == 1)
+ as_bad (_("`fp=xx' cannot be used with `singlefloat'"));
+ break;
+ case 64:
+ if (!ISA_HAS_64BIT_FPRS (opts->isa))
+ as_bad (_("`fp=64' used with a 32-bit fpu"));
+ else if (abi_checks
+ && ABI_NEEDS_32BIT_REGS (mips_abi)
+ && !ISA_HAS_MXHC1 (opts->isa))
+ as_warn (_("`fp=64' used with a 32-bit ABI"));
+ break;
+ case 32:
+ if (abi_checks
+ && ABI_NEEDS_64BIT_REGS (mips_abi))
+ as_warn (_("`fp=32' used with a 64-bit ABI"));
+ if (ISA_IS_R6 (opts->isa) && opts->single_float == 0)
+ as_bad (_("`fp=32' used with a MIPS R6 cpu"));
+ break;
+ default:
+ as_bad (_("Unknown size of floating point registers"));
+ break;
+ }
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi) && !opts->oddspreg)
+ as_bad (_("`nooddspreg` cannot be used with a 64-bit ABI"));
+
+ if (opts->micromips == 1 && opts->mips16 == 1)
+ as_bad (_("`%s' cannot be used with `%s'"), "mips16", "micromips");
+ else if (ISA_IS_R6 (opts->isa)
+ && (opts->micromips == 1
+ || opts->mips16 == 1))
+ as_fatal (_("`%s' cannot be used with `%s'"),
+ opts->micromips ? "micromips" : "mips16",
+ mips_cpu_info_from_isa (opts->isa)->name);
+
+ if (ISA_IS_R6 (opts->isa) && mips_relax_branch)
+ as_fatal (_("branch relaxation is not supported in `%s'"),
+ mips_cpu_info_from_isa (opts->isa)->name);
+}
+
+/* Perform consistency checks on the module level options exactly once.
+ This is a deferred check that happens:
+ at the first .set directive
+ or, at the first pseudo op that generates code (inc .dc.a)
+ or, at the first instruction
+ or, at the end. */
+
+static void
+file_mips_check_options (void)
+{
+ const struct mips_cpu_info *arch_info = 0;
+
+ if (file_mips_opts_checked)
+ return;
+
+ /* The following code determines the register size.
+ Similar code was added to GCC 3.3 (see override_options() in
+ config/mips/mips.c). The GAS and GCC code should be kept in sync
+ as much as possible. */
+
+ if (file_mips_opts.gp < 0)
+ {
+ /* 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_opts.gp = (ABI_NEEDS_32BIT_REGS (mips_abi)
+ || !ISA_HAS_64BIT_REGS (file_mips_opts.isa))
+ ? 32 : 64;
+ }
+
+ if (file_mips_opts.fp < 0)
+ {
+ /* No user specified float register size.
+ ??? GAS treats single-float processors as though they had 64-bit
+ float registers (although it complains when double-precision
+ instructions are used). As things stand, saying they have 32-bit
+ 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_opts.gp == 64)
+ /* 64-bit integer registers implies 64-bit float registers. */
+ file_mips_opts.fp = 64;
+ else if ((file_mips_opts.ase & FP64_ASES)
+ && ISA_HAS_64BIT_FPRS (file_mips_opts.isa))
+ /* Handle ASEs that require 64-bit float registers, if possible. */
+ file_mips_opts.fp = 64;
+ else if (ISA_IS_R6 (mips_opts.isa))
+ /* R6 implies 64-bit float registers. */
+ file_mips_opts.fp = 64;
+ else
+ /* 32-bit float registers. */
+ file_mips_opts.fp = 32;
+ }
+
+ arch_info = mips_cpu_info_from_arch (file_mips_opts.arch);
+
+ /* Disable operations on odd-numbered floating-point registers by default
+ when using the FPXX ABI. */
+ if (file_mips_opts.oddspreg < 0)
+ {
+ if (file_mips_opts.fp == 0)
+ file_mips_opts.oddspreg = 0;
+ else
+ file_mips_opts.oddspreg = 1;
+ }
+
+ /* End of GCC-shared inference code. */
+
+ /* 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 (file_mips_opts.isa)
+ && ((mips_abi == NO_ABI && file_mips_opts.gp == 32)
+ || mips_abi == O32_ABI))
+ mips_32bitmode = 1;
+
+ if (file_mips_opts.isa == ISA_MIPS1 && mips_trap)
+ as_bad (_("trap exception not supported at ISA 1"));
+
+ /* If the selected architecture includes support for ASEs, enable
+ generation of code for them. */
+ if (file_mips_opts.mips16 == -1)
+ file_mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0;
+ if (file_mips_opts.micromips == -1)
+ file_mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch))
+ ? 1 : 0;
+
+ if (mips_nan2008 == -1)
+ mips_nan2008 = (ISA_HAS_LEGACY_NAN (file_mips_opts.isa)) ? 0 : 1;
+ else if (!ISA_HAS_LEGACY_NAN (file_mips_opts.isa) && mips_nan2008 == 0)
+ as_fatal (_("`%s' does not support legacy NaN"),
+ mips_cpu_info_from_arch (file_mips_opts.arch)->name);
+
+ /* Some ASEs require 64-bit FPRs, so -mfp32 should stop those ASEs from
+ being selected implicitly. */
+ if (file_mips_opts.fp != 64)
+ file_ase_explicit |= ASE_MIPS3D | ASE_MDMX | ASE_MSA;
+
+ /* If the user didn't explicitly select or deselect a particular ASE,
+ use the default setting for the CPU. */
+ file_mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
+
+ /* Set up the current options. These may change throughout assembly. */
+ mips_opts = file_mips_opts;
+
+ mips_check_isa_supports_ases ();
+ mips_check_options (&file_mips_opts, TRUE);
+ file_mips_opts_checked = TRUE;
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+ as_warn (_("could not set architecture and machine"));
}
void
bfd_reloc_code_real_type unused_reloc[3]
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+ file_mips_check_options ();
+
imm_expr.X_op = O_absent;
- imm2_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
- imm_reloc[0] = BFD_RELOC_UNUSED;
- imm_reloc[1] = BFD_RELOC_UNUSED;
- imm_reloc[2] = BFD_RELOC_UNUSED;
offset_reloc[0] = BFD_RELOC_UNUSED;
offset_reloc[1] = BFD_RELOC_UNUSED;
offset_reloc[2] = BFD_RELOC_UNUSED;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+ clear_insn_error ();
+
if (mips_opts.mips16)
mips16_ip (str, &insn);
else
str, insn.insn_opcode));
}
- if (insn_error)
- {
- as_bad ("%s `%s'", insn_error, str);
- return;
- }
-
- if (insn.insn_mo->pinfo == INSN_MACRO)
+ if (insn_error.msg)
+ report_insn_error (str);
+ else if (insn.insn_mo->pinfo == INSN_MACRO)
{
macro_start ();
if (mips_opts.mips16)
mips16_macro (&insn);
else
- macro (&insn);
+ macro (&insn, str);
macro_end ();
}
else
{
- if (imm_expr.X_op != O_absent)
- append_insn (&insn, &imm_expr, imm_reloc, FALSE);
- else if (offset_expr.X_op != O_absent)
+ if (offset_expr.X_op != O_absent)
append_insn (&insn, &offset_expr, offset_reloc, FALSE);
else
append_insn (&insn, NULL, unused_reloc, FALSE);
}
+
+ mips_assembling_insn = FALSE;
}
/* Convenience functions for abstracting away the differences between
case BFD_RELOC_MIPS16_HI16_S:
case BFD_RELOC_MIPS16_HI16:
case BFD_RELOC_MIPS16_LO16:
+ case BFD_RELOC_MIPS16_16_PCREL_S1:
return TRUE;
default:
return reloc == BFD_RELOC_MIPS_JMP || reloc == BFD_RELOC_MICROMIPS_JMP;
}
+static inline bfd_boolean
+b_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_MIPS_26_PCREL_S2
+ || reloc == BFD_RELOC_MIPS_21_PCREL_S2
+ || reloc == BFD_RELOC_16_PCREL_S2
+ || reloc == BFD_RELOC_MIPS16_16_PCREL_S1
+ || reloc == BFD_RELOC_MICROMIPS_16_PCREL_S1
+ || reloc == BFD_RELOC_MICROMIPS_10_PCREL_S1
+ || reloc == BFD_RELOC_MICROMIPS_7_PCREL_S1);
+}
+
static inline bfd_boolean
got16_reloc_p (bfd_reloc_code_real_type reloc)
{
return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
}
+static inline bfd_boolean
+gprel16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_GPREL16 || reloc == BFD_RELOC_MIPS16_GPREL
+ || reloc == BFD_RELOC_MICROMIPS_GPREL16);
+}
+
+/* Return true if RELOC is a PC-relative relocation that does not have
+ full address range. */
+
+static inline bfd_boolean
+limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MIPS16_16_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ case BFD_RELOC_MIPS_19_PCREL_S2:
+ return TRUE;
+
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_PCREL:
+ return HAVE_64BIT_ADDRESSES;
+
+ default:
+ return FALSE;
+ }
+}
+
/* Return true if the given relocation might need a matching %lo().
This is only "might" because SVR4 R_MIPS_GOT16 relocations only
need a matching %lo() when applied to local symbols. */
&& 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 insn_labels to the current insertion point. */
+/* Move all labels in LABELS to the current insertion point. TEXT_P
+ says whether the labels refer to text or data. */
static void
-mips_move_labels (void)
+mips_move_labels (struct insn_label_list *labels, bfd_boolean text_p)
{
- segment_info_type *si = seg_info (now_seg);
struct insn_label_list *l;
valueT val;
- for (l = si->label_list; l != NULL; l = l->next)
+ for (l = labels; l != NULL; l = l->next)
{
gas_assert (S_GET_SEGMENT (l->label) == now_seg);
symbol_set_frag (l->label, frag_now);
val = (valueT) frag_now_fix ();
/* MIPS16/microMIPS text labels are stored as odd. */
- if (HAVE_CODE_COMPRESSION)
+ if (text_p && HAVE_CODE_COMPRESSION)
++val;
S_SET_VALUE (l->label, val);
}
}
+/* Move all labels in insn_labels to the current insertion point
+ and treat them as text labels. */
+
+static void
+mips_move_text_labels (void)
+{
+ mips_move_labels (seg_info (now_seg)->label_list, TRUE);
+}
+
static bfd_boolean
s_is_linkonce (symbolS *sym, segT from_seg)
{
{
if ((bfd_get_section_flags (stdoutput, symseg) & SEC_LINK_ONCE))
linkonce = TRUE;
-#ifdef OBJ_ELF
/* The GNU toolchain uses an extension for ELF: a section
beginning with the magic string .gnu.linkonce is a
linkonce section. */
if (strncmp (segment_name (symseg), ".gnu.linkonce",
sizeof ".gnu.linkonce" - 1) == 0)
linkonce = TRUE;
-#endif
}
return linkonce;
}
-/* Mark instruction labels in MIPS16/microMIPS mode. This permits the
+/* Mark MIPS16 or microMIPS instruction label LABEL. This permits the
linker to handle them specially, such as generating jalx instructions
when needed. We also make them odd for the duration of the assembly,
in order to generate the right sort of code. We will make them even
to make them odd again. */
static void
-mips_compressed_mark_labels (void)
+mips_compressed_mark_label (symbolS *label)
{
- segment_info_type *si = seg_info (now_seg);
- struct insn_label_list *l;
-
gas_assert (HAVE_CODE_COMPRESSION);
- for (l = si->label_list; l != NULL; l = l->next)
- {
- symbolS *label = l->label;
+ if (mips_opts.mips16)
+ S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
+ else
+ S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
+ if ((S_GET_VALUE (label) & 1) == 0
+ /* Don't adjust the address if the label is global or weak, or
+ in a link-once section, since we'll be emitting symbol reloc
+ references to it which will be patched up by the linker, and
+ the final value of the symbol may or may not be MIPS16/microMIPS. */
+ && !S_IS_WEAK (label)
+ && !S_IS_EXTERNAL (label)
+ && !s_is_linkonce (label, now_seg))
+ S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+}
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
- if (IS_ELF)
- {
- if (mips_opts.mips16)
- S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
- else
- S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
- }
-#endif
- if ((S_GET_VALUE (label) & 1) == 0
- /* Don't adjust the address if the label is global or weak, or
- in a link-once section, since we'll be emitting symbol reloc
- references to it which will be patched up by the linker, and
- the final value of the symbol may or may not be MIPS16/microMIPS. */
- && ! S_IS_WEAK (label)
- && ! S_IS_EXTERNAL (label)
- && ! s_is_linkonce (label, now_seg))
- S_SET_VALUE (label, S_GET_VALUE (label) | 1);
- }
+/* Mark preceding MIPS16 or microMIPS instruction labels. */
+
+static void
+mips_compressed_mark_labels (void)
+{
+ struct insn_label_list *l;
+
+ for (l = seg_info (now_seg)->label_list; l != NULL; l = l->next)
+ mips_compressed_mark_label (l->label);
}
/* End the current frag. Make it a variant frag and record the
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;
+ return (opcode->pinfo & INSN_READ_ALL) >> INSN_READ_SHIFT;
+}
- mask = 0;
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.micromips)
+/* Return a mask that has bit N set if OPCODE writes to the register(s)
+ in operand N. */
+
+static unsigned int
+insn_write_mask (const struct mips_opcode *opcode)
+{
+ return (opcode->pinfo & INSN_WRITE_ALL) >> INSN_WRITE_SHIFT;
+}
+
+/* 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
+operand_reg_mask (const struct mips_cl_insn *insn,
+ const struct mips_operand *operand,
+ unsigned int type_mask)
+{
+ unsigned int uval, vsel;
+
+ switch (operand->type)
+ {
+ 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_SAME_RS_RT:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ gas_assert ((uval & 31) == (uval >> 5));
+ return 1 << (uval & 31);
+
+ case OP_CHECK_PREV:
+ case OP_NON_ZERO_REG:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return 1 << (uval & 31);
+
+ 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 (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;
+ if (opno_mask & 1)
+ reg_mask |= operand_reg_mask (insn, insn_opno (insn, opno), type_mask);
+ opno_mask >>= 1;
+ opno += 1;
}
- return mask;
+ return reg_mask;
}
/* Return the mask of core registers that IP reads. */
unsigned long pinfo, pinfo2;
unsigned int mask;
- mask = gpr_mod_mask (ip);
+ 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 (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 (pinfo & INSN_UDI)
{
- 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)];
+ /* 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;
}
unsigned long pinfo, pinfo2;
unsigned int mask;
- mask = gpr_mod_mask (ip);
+ 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 (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)
- {
- if (pinfo2 & INSN2_WRITE_GPR_MB)
- mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
- if (pinfo2 & INSN2_WRITE_GPR_MHI)
- {
- mask |= 1 << micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
- mask |= 1 << micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *ip)];
- }
- 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);
- }
+ 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;
}
static unsigned int
fpr_read_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_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);
- }
/* 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;
}
-/* Classify an instruction according to the FIX_VR4120_* enumeration.
- Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
- by VR4120 errata. */
+/* Operand OPNUM of INSN is an odd-numbered floating-point register.
+ Check whether that is allowed. */
-static unsigned int
-classify_vr4120_insn (const char *name)
+static bfd_boolean
+mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
{
- if (strncmp (name, "macc", 4) == 0)
- return FIX_VR4120_MACC;
- if (strncmp (name, "dmacc", 5) == 0)
- return FIX_VR4120_DMACC;
- if (strncmp (name, "mult", 4) == 0)
- return FIX_VR4120_MULT;
- if (strncmp (name, "dmult", 5) == 0)
- return FIX_VR4120_DMULT;
- if (strstr (name, "div"))
- return FIX_VR4120_DIV;
- if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
- return FIX_VR4120_MTHILO;
- return NUM_FIX_VR4120_CLASSES;
+ const char *s = insn->name;
+ bfd_boolean oddspreg = (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa, mips_opts.arch)
+ || FPR_SIZE == 64)
+ && mips_opts.oddspreg;
+
+ if (insn->pinfo == INSN_MACRO)
+ /* Let a macro pass, we'll catch it later when it is expanded. */
+ return TRUE;
+
+ /* Single-precision coprocessor loads and moves are OK for 32-bit registers,
+ otherwise it depends on oddspreg. */
+ if ((insn->pinfo & FP_S)
+ && (insn->pinfo & (INSN_LOAD_MEMORY | INSN_STORE_MEMORY
+ | INSN_LOAD_COPROC | INSN_COPROC_MOVE)))
+ return FPR_SIZE == 32 || oddspreg;
+
+ /* Allow odd registers for single-precision ops and double-precision if the
+ floating-point registers are 64-bit wide. */
+ switch (insn->pinfo & (FP_S | FP_D))
+ {
+ case FP_S:
+ case 0:
+ return oddspreg;
+ case FP_D:
+ return FPR_SIZE == 64;
+ default:
+ break;
+ }
+
+ /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */
+ s = strchr (insn->name, '.');
+ if (s != NULL && opnum == 2)
+ s = strchr (s + 1, '.');
+ if (s != NULL && (s[1] == 'w' || s[1] == 's'))
+ return oddspreg;
+
+ return FPR_SIZE == 64;
}
-#define INSN_ERET 0x42000018
-#define INSN_DERET 0x4200001f
+/* Information about an instruction argument that we're trying to match. */
+struct mips_arg_info
+{
+ /* The instruction so far. */
+ struct mips_cl_insn *insn;
-/* Return the number of instructions that must separate INSN1 and INSN2,
- where INSN1 is the earlier instruction. Return the worst-case value
- for any INSN2 if INSN2 is null. */
+ /* The first unconsumed operand token. */
+ struct mips_operand_token *token;
-static unsigned int
-insns_between (const struct mips_cl_insn *insn1,
- const struct mips_cl_insn *insn2)
+ /* The 1-based operand number, in terms of insn->insn_mo->args. */
+ int opnum;
+
+ /* The 1-based argument number, for error reporting. This does not
+ count elided optional registers, etc.. */
+ int argnum;
+
+ /* The last OP_REG operand seen, or ILLEGAL_REG if none. */
+ unsigned int last_regno;
+
+ /* If the first operand was an OP_REG, this is the register that it
+ specified, otherwise it is ILLEGAL_REG. */
+ unsigned int dest_regno;
+
+ /* The value of the last OP_INT operand. Only used for OP_MSB,
+ where it gives the lsb position. */
+ unsigned int last_op_int;
+
+ /* If true, match routines should assume that no later instruction
+ alternative matches and should therefore be as accommodating 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;
+};
+
+/* Record that the argument is out of range. */
+
+static void
+match_out_of_range (struct mips_arg_info *arg)
{
- unsigned long pinfo1, pinfo2;
- unsigned int mask;
+ set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum);
+}
- /* This function needs to know which pinfo flags are set for INSN2
- and which registers INSN2 uses. The former is stored in PINFO2 and
- the latter is tested via INSN2_USES_GPR. If INSN2 is null, PINFO2
- will have every flag set and INSN2_USES_GPR will always return true. */
- pinfo1 = insn1->insn_mo->pinfo;
- pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
+/* Record that the argument isn't constant but needs to be. */
-#define INSN2_USES_GPR(REG) \
- (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
+static void
+match_not_constant (struct mips_arg_info *arg)
+{
+ set_insn_error_i (arg->argnum, _("operand %d must be constant"),
+ arg->argnum);
+}
- /* For most targets, write-after-read dependencies on the HI and LO
- registers must be separated by at least two instructions. */
- if (!hilo_interlocks)
+/* 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)
{
- if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
- return 2;
- if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
- return 2;
+ ++arg->token;
+ if (ch == ',')
+ arg->argnum += 1;
+ return TRUE;
}
+ return FALSE;
+}
- /* If we're working around r7000 errata, there must be two instructions
- between an mfhi or mflo and any instruction that uses the result. */
- if (mips_7000_hilo_fix
- && !mips_opts.micromips
- && MF_HILO_INSN (pinfo1)
- && INSN2_USES_GPR (EXTRACT_OPERAND (0, RD, *insn1)))
- return 2;
+/* Try to get an expression from the next tokens in ARG. Consume the
+ tokens and return true on success, storing the expression value in
+ VALUE and relocation types in R. */
- /* If we're working around 24K errata, one instruction is required
- if an ERET or DERET is followed by a branch instruction. */
- if (mips_fix_24k && !mips_opts.micromips)
+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 == '(')
{
- if (insn1->insn_opcode == INSN_ERET
- || insn1->insn_opcode == INSN_DERET)
- {
- if (insn2 == NULL
- || insn2->insn_opcode == INSN_ERET
- || insn2->insn_opcode == INSN_DERET
- || delayed_branch_p (insn2))
- return 1;
- }
+ value->X_op = O_constant;
+ value->X_add_number = 0;
+ r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+ return TRUE;
}
- /* If working around VR4120 errata, check for combinations that need
- a single intervening instruction. */
- if (mips_fix_vr4120 && !mips_opts.micromips)
+ /* 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)
{
- unsigned int class1, class2;
-
- class1 = classify_vr4120_insn (insn1->insn_mo->name);
- if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
- {
- if (insn2 == NULL)
- return 1;
- class2 = classify_vr4120_insn (insn2->insn_mo->name);
- if (vr4120_conflicts[class1] & (1 << class2))
- return 1;
- }
+ set_insn_error (arg->argnum, _("register value used as expression"));
+ return FALSE;
}
- if (!HAVE_CODE_COMPRESSION)
+ if (arg->token->type == OT_INTEGER)
{
- /* Check for GPR or coprocessor load delays. All such delays
- are on the RT register. */
- /* Itbl support may require additional care here. */
- if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
- || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
- {
- know (pinfo1 & INSN_WRITE_GPR_T);
- if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
- return 1;
- }
+ *value = arg->token->u.integer.value;
+ memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+ ++arg->token;
+ return TRUE;
+ }
- /* Check for generic coprocessor hazards.
+ set_insn_error_i
+ (arg->argnum, _("operand %d must be an immediate expression"),
+ arg->argnum);
+ return FALSE;
+}
- This case is not handled very well. There is no special
- knowledge of CP0 handling, and the coprocessors other than
- the floating point unit are not distinguished at all. */
- /* Itbl support may require additional care here. FIXME!
- Need to modify this to include knowledge about
- user specified delays! */
- else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY))
- || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
- {
- /* Handle cases where INSN1 writes to a known general coprocessor
- register. There must be a one instruction delay before INSN2
- if INSN2 reads that register, otherwise no delay is needed. */
- mask = fpr_write_mask (insn1);
- if (mask != 0)
- {
- if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
- return 1;
- }
- else
- {
- /* Read-after-write dependencies on the control registers
- require a two-instruction gap. */
- if ((pinfo1 & INSN_WRITE_COND_CODE)
- && (pinfo2 & INSN_READ_COND_CODE))
- return 2;
+/* 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. */
- /* We don't know exactly what INSN1 does. If INSN2 is
- also a coprocessor instruction, assume there must be
- a one instruction gap. */
- if (pinfo2 & INSN_COP)
- return 1;
- }
- }
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value)
+{
+ expressionS ex;
+ bfd_reloc_code_real_type r[3];
- /* Check for read-after-write dependencies on the coprocessor
- control registers in cases where INSN1 does not need a general
- coprocessor delay. This means that INSN1 is a floating point
- comparison instruction. */
- /* Itbl support may require additional care here. */
- else if (!cop_interlocks
- && (pinfo1 & INSN_WRITE_COND_CODE)
- && (pinfo2 & INSN_READ_COND_CODE))
- return 1;
+ 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
+ {
+ match_not_constant (arg);
+ return FALSE;
}
+ return TRUE;
+}
-#undef INSN2_USES_GPR
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+ appears in instruction OPCODE. */
- return 0;
+static unsigned int
+convert_reg_type (const struct mips_opcode *opcode,
+ enum mips_reg_operand_type type)
+{
+ switch (type)
+ {
+ case OP_REG_GP:
+ return RTYPE_NUM | RTYPE_GP;
+
+ case OP_REG_FP:
+ /* Allow vector register names for MDMX if the instruction is a 64-bit
+ FPR load, store or move (including moves to and from GPRs). */
+ if ((mips_opts.ase & ASE_MDMX)
+ && (opcode->pinfo & FP_D)
+ && (opcode->pinfo & (INSN_COPROC_MOVE
+ | INSN_COPROC_MEMORY_DELAY
+ | INSN_LOAD_COPROC
+ | INSN_LOAD_MEMORY
+ | INSN_STORE_MEMORY)))
+ return RTYPE_FPU | RTYPE_VEC;
+ return RTYPE_FPU;
+
+ case OP_REG_CCC:
+ if (opcode->pinfo & (FP_D | FP_S))
+ return RTYPE_CCC | RTYPE_FCC;
+ return RTYPE_CCC;
+
+ case OP_REG_VEC:
+ if (opcode->membership & INSN_5400)
+ return RTYPE_FPU;
+ return RTYPE_FPU | RTYPE_VEC;
+
+ case OP_REG_ACC:
+ return RTYPE_ACC;
+
+ case OP_REG_COPRO:
+ if (opcode->name[strlen (opcode->name) - 1] == '0')
+ return RTYPE_NUM | RTYPE_CP0;
+ return RTYPE_NUM;
+
+ case OP_REG_HW:
+ return RTYPE_NUM;
+
+ 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 ();
}
-/* Return the number of nops that would be needed to work around the
- VR4130 mflo/mfhi errata if instruction INSN immediately followed
- the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
- that are contained within the first IGNORE instructions of HIST. */
+/* ARG is register REGNO, of type TYPE. Warn about any dubious registers. */
-static int
-nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static void
+check_regno (struct mips_arg_info *arg,
+ enum mips_reg_operand_type type, unsigned int regno)
{
- int i, j;
- unsigned int mask;
+ if (AT && type == OP_REG_GP && regno == AT)
+ arg->seen_at = TRUE;
- /* Check if the instruction writes to HI or LO. MTHI and MTLO
- are not affected by the errata. */
- if (insn != 0
- && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
- || strcmp (insn->insn_mo->name, "mtlo") == 0
- || strcmp (insn->insn_mo->name, "mthi") == 0))
- return 0;
+ if (type == OP_REG_FP
+ && (regno & 1) != 0
+ && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
+ {
+ /* This was a warning prior to introducing O32 FPXX and FP64 support
+ so maintain a warning for FP32 but raise an error for the new
+ cases. */
+ if (FPR_SIZE == 32)
+ as_warn (_("float register should be even, was %d"), regno);
+ else
+ as_bad (_("float register should be even, was %d"), regno);
+ }
- /* Search for the first MFLO or MFHI. */
- for (i = 0; i < MAX_VR4130_NOPS; i++)
- if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
- {
- /* Extract the destination register. */
- mask = gpr_write_mask (&hist[i]);
+ if (type == OP_REG_CCC)
+ {
+ const char *name;
+ size_t length;
+
+ name = arg->insn->insn_mo->name;
+ length = strlen (name);
+ if ((regno & 1) != 0
+ && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0)
+ || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0)))
+ as_warn (_("condition code register should be even for %s, was %d"),
+ name, regno);
+
+ if ((regno & 3) != 0
+ && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0))
+ as_warn (_("condition code register should be 0 or 4 for %s, was %d"),
+ name, regno);
+ }
+}
- /* No nops are needed if INSN reads that register. */
- if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
- return 0;
+/* 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. */
- /* ...or if any of the intervening instructions do. */
- for (j = 0; j < i; j++)
- if (gpr_read_mask (&hist[j]) & mask)
- return 0;
+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;
- if (i >= ignore)
- return MAX_VR4130_NOPS - i;
- }
- return 0;
+ *regno = symval & RNUM_MASK;
+ check_regno (arg, type, *regno);
+ return TRUE;
}
-#define BASE_REG_EQ(INSN1, INSN2) \
- ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
- == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+/* 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. */
-/* Return the minimum alignment for this store instruction. */
+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;
+}
-static int
-fix_24k_align_to (const struct mips_opcode *mo)
+/* 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 (strcmp (mo->name, "sh") == 0)
- return 2;
+ 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;
+}
- if (strcmp (mo->name, "swc1") == 0
- || strcmp (mo->name, "swc2") == 0
- || strcmp (mo->name, "sw") == 0
- || strcmp (mo->name, "sc") == 0
- || strcmp (mo->name, "s.s") == 0)
- return 4;
+/* OP_INT matcher. */
- if (strcmp (mo->name, "sdc1") == 0
- || strcmp (mo->name, "sdc2") == 0
- || strcmp (mo->name, "s.d") == 0)
- return 8;
+static bfd_boolean
+match_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_int_operand *operand;
+ unsigned int uval;
+ int min_val, max_val, factor;
+ offsetT sval;
+
+ operand = (const struct mips_int_operand *) operand_base;
+ factor = 1 << 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->shift == 0
+ && operand->bias == 0
+ && (operand->max_val == 32767 || operand->max_val == 65535))
+ {
+ /* The operand can be relocated. */
+ if (!match_expression (arg, &offset_expr, offset_reloc))
+ return FALSE;
+
+ if (offset_reloc[0] != BFD_RELOC_UNUSED)
+ /* Relocation operators were used. Accept the arguent and
+ leave the relocation value in offset_expr and offset_relocs
+ for the caller to process. */
+ return TRUE;
- /* sb, swl, swr */
- return 1;
-}
+ if (offset_expr.X_op != O_constant)
+ {
+ /* 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;
+ }
-struct fix_24k_store_info
- {
- /* Immediate offset, if any, for this store instruction. */
- short off;
- /* Alignment required by this store instruction. */
- int align_to;
- /* True for register offsets. */
- int register_offset;
- };
+ /* Clear the global state; we're going to install the operand
+ ourselves. */
+ sval = offset_expr.X_add_number;
+ offset_expr.X_op = O_absent;
-/* Comparison function used by qsort. */
+ /* 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
+ {
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+ }
-static int
-fix_24k_sort (const void *a, const void *b)
-{
- const struct fix_24k_store_info *pos1 = a;
- const struct fix_24k_store_info *pos2 = b;
+ arg->last_op_int = sval;
- return (pos1->off - pos2->off);
+ if (sval < min_val || sval > max_val || sval % factor)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ uval = (unsigned int) sval >> operand->shift;
+ uval -= operand->bias;
+
+ /* Handle -mfix-cn63xxp1. */
+ if (arg->opnum == 1
+ && mips_fix_cn63xxp1
+ && !mips_opts.micromips
+ && strcmp ("pref", arg->insn->insn_mo->name) == 0)
+ switch (uval)
+ {
+ case 5:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ /* These are ok. */
+ break;
+
+ default:
+ /* The rest must be changed to 28. */
+ uval = 28;
+ break;
+ }
+
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
}
-/* INSN is a store instruction. Try to record the store information
- in STINFO. Return false if the information isn't known. */
+/* OP_MAPPED_INT matcher. */
static bfd_boolean
-fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
- const struct mips_cl_insn *insn)
+match_mapped_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
{
- /* The instruction must have a known offset. */
- if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+ const struct mips_mapped_int_operand *operand;
+ unsigned int uval, num_vals;
+ offsetT sval;
+
+ operand = (const struct mips_mapped_int_operand *) operand_base;
+ if (!match_const_int (arg, &sval))
return FALSE;
- stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
- stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+ num_vals = 1 << operand_base->size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->int_map[uval] == sval)
+ break;
+ if (uval == num_vals)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ insn_insert_operand (arg->insn, operand_base, uval);
return TRUE;
}
-/* Return the number of nops that would be needed to work around the 24k
- "lost data on stores during refill" errata if instruction INSN
- immediately followed the 2 instructions described by HIST.
- Ignore hazards that are contained within the first IGNORE
- instructions of HIST.
+/* OP_MSB matcher. */
- Problem: The FSB (fetch store buffer) acts as an intermediate buffer
- for the data cache refills and store data. The following describes
- the scenario where the store data could be lost.
+static bfd_boolean
+match_msb_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_msb_operand *operand;
+ int min_val, max_val, max_high;
+ offsetT size, sval, high;
- * A data cache miss, due to either a load or a store, causing fill
- data to be supplied by the memory subsystem
- * The first three doublewords of fill data are returned and written
- into the cache
- * A sequence of four stores occurs in consecutive cycles around the
- final doubleword of the fill:
- * Store A
- * Store B
- * Store C
- * Zero, One or more instructions
- * Store D
+ operand = (const struct mips_msb_operand *) operand_base;
+ min_val = operand->bias;
+ max_val = min_val + (1 << operand_base->size) - 1;
+ max_high = operand->opsize;
- The four stores A-D must be to different doublewords of the line that
- is being filled. The fourth instruction in the sequence above permits
- the fill of the final doubleword to be transferred from the FSB into
- the cache. In the sequence above, the stores may be either integer
- (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
- swxc1, sdxc1, suxc1) stores, as long as the four stores are to
- different doublewords on the line. If the floating point unit is
- running in 1:2 mode, it is not possible to create the sequence above
- using only floating point store instructions.
+ if (!match_const_int (arg, &size))
+ return FALSE;
- In this case, the cache line being filled is incorrectly marked
- invalid, thereby losing the data from any store to the line that
- occurs between the original miss and the completion of the five
- cycle sequence shown above.
+ high = size + arg->last_op_int;
+ sval = operand->add_lsb ? high : size;
- The workarounds are:
+ if (size < 0 || high > max_high || sval < min_val || sval > max_val)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ insn_insert_operand (arg->insn, operand_base, sval - min_val);
+ return TRUE;
+}
- * Run the data cache in write-through mode.
- * Insert a non-store instruction between
- Store A and Store B or Store B and Store C. */
-
-static int
-nops_for_24k (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
-{
- struct fix_24k_store_info pos[3];
- int align, i, base_offset;
+/* OP_REG matcher. */
- if (ignore >= 2)
- return 0;
+static bfd_boolean
+match_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_reg_operand *operand;
+ unsigned int regno, uval, num_vals;
- /* If the previous instruction wasn't a store, there's nothing to
- worry about. */
- if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
- return 0;
+ operand = (const struct mips_reg_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, ®no))
+ return FALSE;
- /* If the instructions after the previous one are unknown, we have
- to assume the worst. */
- if (!insn)
- return 1;
+ if (operand->reg_map)
+ {
+ num_vals = 1 << operand->root.size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->reg_map[uval] == regno)
+ break;
+ if (num_vals == uval)
+ return FALSE;
+ }
+ else
+ uval = regno;
- /* Check whether we are dealing with three consecutive stores. */
- if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
- || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
- return 0;
+ arg->last_regno = regno;
+ if (arg->opnum == 1)
+ arg->dest_regno = regno;
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
- /* If we don't know the relationship between the store addresses,
- assume the worst. */
- if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
- || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
- return 1;
+/* OP_REG_PAIR matcher. */
- if (!fix_24k_record_store_info (&pos[0], insn)
- || !fix_24k_record_store_info (&pos[1], &hist[0])
- || !fix_24k_record_store_info (&pos[2], &hist[1]))
- return 1;
+static bfd_boolean
+match_reg_pair_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_reg_pair_operand *operand;
+ unsigned int regno1, regno2, uval, num_vals;
- qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+ operand = (const struct mips_reg_pair_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, ®no1)
+ || !match_char (arg, ',')
+ || !match_reg (arg, operand->reg_type, ®no2))
+ return FALSE;
- /* Pick a value of ALIGN and X such that all offsets are adjusted by
- X bytes and such that the base register + X is known to be aligned
- to align bytes. */
+ num_vals = 1 << operand_base->size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2)
+ break;
+ if (uval == num_vals)
+ return FALSE;
- if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
- align = 8;
- else
- {
- align = pos[0].align_to;
- base_offset = pos[0].off;
- for (i = 1; i < 3; i++)
- if (align < pos[i].align_to)
- {
- align = pos[i].align_to;
- base_offset = pos[i].off;
- }
- for (i = 0; i < 3; i++)
- pos[i].off -= base_offset;
- }
-
- pos[0].off &= ~align + 1;
- pos[1].off &= ~align + 1;
- pos[2].off &= ~align + 1;
-
- /* If any two stores write to the same chunk, they also write to the
- same doubleword. The offsets are still sorted at this point. */
- if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
- return 0;
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
- /* A range of at least 9 bytes is needed for the stores to be in
- non-overlapping doublewords. */
- if (pos[2].off - pos[0].off <= 8)
- return 0;
+/* OP_PCREL matcher. The caller chooses the relocation type. */
- if (pos[2].off - pos[1].off >= 24
- || pos[1].off - pos[0].off >= 24
- || pos[2].off - pos[0].off >= 32)
- return 0;
+static bfd_boolean
+match_pcrel_operand (struct mips_arg_info *arg)
+{
+ bfd_reloc_code_real_type r[3];
- return 1;
+ return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
}
-/* Return the number of nops that would be needed if instruction INSN
- immediately followed the MAX_NOPS instructions given by HIST,
- where HIST[0] is the most recent instruction. Ignore hazards
- between INSN and the first IGNORE instructions in HIST.
-
- If INSN is null, return the worse-case number of nops for any
- instruction. */
+/* OP_PERF_REG matcher. */
-static int
-nops_for_insn (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static bfd_boolean
+match_perf_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- int i, nops, tmp_nops;
+ offsetT sval;
- nops = 0;
- for (i = ignore; i < MAX_DELAY_NOPS; i++)
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+
+ if (sval != 0
+ && (sval != 1
+ || (mips_opts.arch == CPU_R5900
+ && (strcmp (arg->insn->insn_mo->name, "mfps") == 0
+ || strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
{
- tmp_nops = insns_between (hist + i, insn) - i;
- if (tmp_nops > nops)
- nops = tmp_nops;
+ set_insn_error (arg->argnum, _("invalid performance register"));
+ return FALSE;
}
- if (mips_fix_vr4130 && !mips_opts.micromips)
+ insn_insert_operand (arg->insn, operand, sval);
+ return TRUE;
+}
+
+/* OP_ADDIUSP matcher. */
+
+static bfd_boolean
+match_addiusp_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ offsetT sval;
+ unsigned int uval;
+
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+
+ if (sval % 4)
{
- tmp_nops = nops_for_vr4130 (ignore, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
+ match_out_of_range (arg);
+ return FALSE;
}
- if (mips_fix_24k && !mips_opts.micromips)
+ sval /= 4;
+ if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
{
- tmp_nops = nops_for_24k (ignore, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
+ match_out_of_range (arg);
+ return FALSE;
}
- return nops;
+ uval = (unsigned int) sval;
+ uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
}
-/* The variable arguments provide NUM_INSNS extra instructions that
- might be added to HIST. Return the largest number of nops that
- would be needed after the extended sequence, ignoring hazards
- in the first IGNORE instructions. */
+/* OP_CLO_CLZ_DEST matcher. */
-static int
-nops_for_sequence (int num_insns, int ignore,
- const struct mips_cl_insn *hist, ...)
+static bfd_boolean
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- va_list args;
- struct mips_cl_insn buffer[MAX_NOPS];
- struct mips_cl_insn *cursor;
- int nops;
+ unsigned int regno;
- va_start (args, hist);
- cursor = buffer + num_insns;
- memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
- while (cursor > buffer)
- *--cursor = *va_arg (args, const struct mips_cl_insn *);
+ if (!match_reg (arg, OP_REG_GP, ®no))
+ return FALSE;
- nops = nops_for_insn (ignore, buffer, NULL);
- va_end (args);
- return nops;
+ insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+ return TRUE;
}
-/* Like nops_for_insn, but if INSN is a branch, take into account the
- worst-case delay for the branch target. */
+/* OP_CHECK_PREV matcher. */
-static int
-nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static bfd_boolean
+match_check_prev_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
{
- int nops, tmp_nops;
+ const struct mips_check_prev_operand *operand;
+ unsigned int regno;
- nops = nops_for_insn (ignore, hist, insn);
- if (delayed_branch_p (insn))
- {
- tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
- hist, insn, get_delay_slot_nop (insn));
- if (tmp_nops > nops)
- nops = tmp_nops;
- }
- else if (compact_branch_p (insn))
+ operand = (const struct mips_check_prev_operand *) operand_base;
+
+ if (!match_reg (arg, OP_REG_GP, ®no))
+ return FALSE;
+
+ if (!operand->zero_ok && regno == 0)
+ return FALSE;
+
+ if ((operand->less_than_ok && regno < arg->last_regno)
+ || (operand->greater_than_ok && regno > arg->last_regno)
+ || (operand->equal_ok && regno == arg->last_regno))
{
- tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
+ arg->last_regno = regno;
+ insn_insert_operand (arg->insn, operand_base, regno);
+ return TRUE;
}
- return nops;
+
+ return FALSE;
}
-/* Fix NOP issue: Replace nops by "or at,at,zero". */
+/* OP_SAME_RS_RT matcher. */
-static void
-fix_loongson2f_nop (struct mips_cl_insn * ip)
+static bfd_boolean
+match_same_rs_rt_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- gas_assert (!HAVE_CODE_COMPRESSION);
- if (strcmp (ip->insn_mo->name, "nop") == 0)
- ip->insn_opcode = LOONGSON2F_NOP_INSN;
+ unsigned int regno;
+
+ if (!match_reg (arg, OP_REG_GP, ®no))
+ return FALSE;
+
+ if (regno == 0)
+ {
+ set_insn_error (arg->argnum, _("the source register must not be $0"));
+ return FALSE;
+ }
+
+ arg->last_regno = regno;
+
+ insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+ return TRUE;
}
-/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
- jr target pc &= 'hffff_ffff_cfff_ffff. */
+/* OP_LWM_SWM_LIST matcher. */
-static void
-fix_loongson2f_jump (struct mips_cl_insn * ip)
+static bfd_boolean
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- gas_assert (!HAVE_CODE_COMPRESSION);
- if (strcmp (ip->insn_mo->name, "j") == 0
- || strcmp (ip->insn_mo->name, "jr") == 0
- || strcmp (ip->insn_mo->name, "jalr") == 0)
+ unsigned int reglist, sregs, ra, regno1, regno2;
+ struct mips_arg_info reset;
+
+ reglist = 0;
+ if (!match_reg_range (arg, OP_REG_GP, ®no1, ®no2))
+ return FALSE;
+ do
{
- int sreg;
- expressionS ep;
+ 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 (! mips_opts.at)
- return;
+ if (operand->size == 2)
+ {
+ /* The list must include both ra and s0-sN, for 0 <= N <= 3. E.g.:
- sreg = EXTRACT_OPERAND (0, RS, *ip);
- if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
- return;
+ s0, ra
+ s0, s1, ra, s2, s3
+ s0-s2, ra
- ep.X_op = O_constant;
- ep.X_add_number = 0xcfff0000;
- macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
- ep.X_add_number = 0xffff;
- macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
- macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
+ and any permutations of these. */
+ if ((reglist & 0xfff1ffff) != 0x80010000)
+ return FALSE;
+
+ sregs = (reglist >> 17) & 7;
+ ra = 0;
+ }
+ else
+ {
+ /* The list must include at least one of ra and s0-sN,
+ for 0 <= N <= 8. (Note that there is a gap between s7 and s8,
+ which are $23 and $30 respectively.) E.g.:
+
+ ra
+ s0
+ ra, s0, s1, s2
+ s0-s8
+ s0-s5, ra
+
+ and any permutations of these. */
+ if ((reglist & 0x3f00ffff) != 0)
+ return FALSE;
+
+ ra = (reglist >> 27) & 0x10;
+ sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
}
+ sregs += 1;
+ if ((sregs & -sregs) != sregs)
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+ return TRUE;
}
-static void
-fix_loongson2f (struct mips_cl_insn * ip)
+/* OP_ENTRY_EXIT_LIST matcher. */
+
+static unsigned int
+match_entry_exit_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- if (mips_fix_loongson2f_nop)
- fix_loongson2f_nop (ip);
+ unsigned int mask;
+ bfd_boolean is_exit;
- if (mips_fix_loongson2f_jump)
- fix_loongson2f_jump (ip);
+ /* 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;
}
-/* IP is a branch that has a delay slot, and we need to fill it
- automatically. Return true if we can do that by swapping IP
- with the previous instruction. */
+/* OP_SAVE_RESTORE_LIST matcher. */
static bfd_boolean
-can_swap_branch_p (struct mips_cl_insn *ip)
+match_save_restore_list_operand (struct mips_arg_info *arg)
{
- unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
- unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+ 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;
- /* -O2 and above is required for this optimization. */
- if (mips_optimize < 2)
- return FALSE;
+ 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;
- /* If we have seen .set volatile or .set nomove, don't optimize. */
- if (mips_opts.nomove)
- 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, ','));
- /* We can't swap if the previous instruction's position is fixed. */
- if (history[0].fixed_p)
+ /* 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;
- /* If the previous previous insn was in a .set noreorder, we can't
- swap. Actually, the MIPS assembler will swap in this situation.
- However, gcc configured -with-gnu-as will generate code like
+ /* Count static registers. */
+ num_statics = 0;
+ while (statics & 0x8)
+ {
+ statics = (statics << 1) & 0xf;
+ num_statics += 1;
+ }
+ if (statics != 0)
+ return FALSE;
- .set noreorder
- lw $4,XXX
- .set reorder
- INSN
- bne $4,$0,foo
+ /* Encode args/statics. */
+ opcode |= ((num_args << 2) | num_statics) << 16;
+ }
- in which we can not swap the bne and INSN. If gcc is not configured
- -with-gnu-as, it does not output the .set pseudo-ops. */
- if (history[1].noreorder_p)
- return FALSE;
+ /* Encode $s0/$s1. */
+ if (sregs & (1 << 0)) /* $s0 */
+ opcode |= 0x20;
+ if (sregs & (1 << 1)) /* $s1 */
+ opcode |= 0x10;
+ sregs >>= 2;
- /* If the previous instruction had a fixup in mips16 mode, we can not swap.
- This means that the previous instruction was a 4-byte one anyhow. */
- if (mips_opts.mips16 && history[0].fixp[0])
+ /* Encode $s2-$s8. */
+ num_sregs = 0;
+ while (sregs & 1)
+ {
+ sregs >>= 1;
+ num_sregs += 1;
+ }
+ if (sregs != 0)
return FALSE;
+ opcode |= num_sregs << 24;
- /* If the branch is itself the target of a branch, we can not swap.
- We cheat on this; all we check for is whether there is a label on
- this instruction. If there are any branches to anything other than
- a label, users must use .set noreorder. */
- if (seg_info (now_seg)->label_list)
- return FALSE;
+ /* 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));
+ }
- /* If the previous instruction is in a variant frag other than this
- branch's one, we cannot do the swap. This does not apply to
- MIPS16 code, which uses variant frags for different purposes. */
- if (!mips_opts.mips16
- && history[0].frag
- && history[0].frag->fr_type == rs_machine_dependent)
- return FALSE;
+ /* Finally build the instruction. */
+ if ((opcode >> 16) != 0 || frame_size == 0)
+ opcode |= MIPS16_EXTEND;
+ arg->insn->insn_opcode = opcode;
+ return TRUE;
+}
- /* We do not swap with instructions that cannot architecturally
- be placed in a branch delay slot, such as SYNC or ERET. We
- also refrain from swapping with a trap instruction, since it
- complicates trap handlers to have the trap instruction be in
- a delay slot. */
- prev_pinfo = history[0].insn_mo->pinfo;
- if (prev_pinfo & INSN_NO_DELAY_SLOT)
- return FALSE;
+/* OP_MDMX_IMM_REG matcher. */
- /* Check for conflicts between the branch and the instructions
- before the candidate delay slot. */
- if (nops_for_insn (0, history + 1, ip) > 0)
- return FALSE;
+static bfd_boolean
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno, uval;
+ bfd_boolean is_qh;
+ const struct mips_opcode *opcode;
- /* Check for conflicts between the swapped sequence and the
- target of the branch. */
- if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
- return FALSE;
+ /* The mips_opcode records whether this is an octobyte or quadhalf
+ instruction. Start out with that bit in place. */
+ opcode = arg->insn->insn_mo;
+ uval = mips_extract_operand (operand, opcode->match);
+ is_qh = (uval != 0);
- /* If the branch reads a register that the previous
- instruction sets, we can not swap. */
- gpr_read = gpr_read_mask (ip);
- prev_gpr_write = gpr_write_mask (&history[0]);
- if (gpr_read & prev_gpr_write)
- return FALSE;
+ if (arg->token->type == OT_REG)
+ {
+ if ((opcode->membership & INSN_5400)
+ && strcmp (opcode->name, "rzu.ob") == 0)
+ {
+ set_insn_error_i (arg->argnum, _("operand %d must be an immediate"),
+ arg->argnum);
+ return FALSE;
+ }
- /* If the branch writes a register that the previous
- instruction sets, we can not swap. */
- gpr_write = gpr_write_mask (ip);
- if (gpr_write & prev_gpr_write)
- return FALSE;
+ if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, ®no))
+ return FALSE;
+ ++arg->token;
- /* If the branch writes a register that the previous
- instruction reads, we can not swap. */
- prev_gpr_read = gpr_read_mask (&history[0]);
- if (gpr_write & prev_gpr_read)
- return FALSE;
+ /* Check whether this is a vector register or a broadcast of
+ a single element. */
+ if (arg->token->type == OT_INTEGER_INDEX)
+ {
+ if (arg->token->u.index > (is_qh ? 3 : 7))
+ {
+ set_insn_error (arg->argnum, _("invalid element selector"));
+ return FALSE;
+ }
+ uval |= arg->token->u.index << (is_qh ? 2 : 1) << 5;
+ ++arg->token;
+ }
+ else
+ {
+ /* A full vector. */
+ if ((opcode->membership & INSN_5400)
+ && (strcmp (opcode->name, "sll.ob") == 0
+ || strcmp (opcode->name, "srl.ob") == 0))
+ {
+ set_insn_error_i (arg->argnum, _("operand %d must be scalar"),
+ arg->argnum);
+ return FALSE;
+ }
- /* If one instruction sets a condition code and the
- other one uses a condition code, we can not swap. */
- pinfo = ip->insn_mo->pinfo;
- if ((pinfo & INSN_READ_COND_CODE)
- && (prev_pinfo & INSN_WRITE_COND_CODE))
- return FALSE;
- if ((pinfo & INSN_WRITE_COND_CODE)
- && (prev_pinfo & INSN_READ_COND_CODE))
- return FALSE;
+ if (is_qh)
+ uval |= MDMX_FMTSEL_VEC_QH << 5;
+ else
+ uval |= MDMX_FMTSEL_VEC_OB << 5;
+ }
+ uval |= regno;
+ }
+ else
+ {
+ offsetT sval;
- /* If the previous instruction uses the PC, we can not swap. */
- prev_pinfo2 = history[0].insn_mo->pinfo2;
- if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
- return FALSE;
- if (mips_opts.micromips && (prev_pinfo2 & INSN2_READ_PC))
- return FALSE;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+ if (sval < 0 || sval > 31)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ uval |= (sval & 31);
+ if (is_qh)
+ uval |= MDMX_FMTSEL_IMM_QH << 5;
+ else
+ uval |= MDMX_FMTSEL_IMM_OB << 5;
+ }
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
- /* If the previous instruction has an incorrect size for a fixed
- branch delay slot in microMIPS mode, we cannot swap. */
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.micromips
- && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
- && insn_length (history) != 2)
- return FALSE;
- if (mips_opts.micromips
- && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
- && insn_length (history) != 4)
+/* 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;
}
-/* Decide how we should add IP to the instruction stream. */
+/* OP_REG_INDEX matcher. */
-static enum append_method
-get_append_method (struct mips_cl_insn *ip)
+static bfd_boolean
+match_reg_index_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- unsigned long pinfo;
+ unsigned int regno;
- /* The relaxed version of a macro sequence must be inherently
- hazard-free. */
- if (mips_relax.sequence == 2)
- return APPEND_ADD;
+ if (arg->token->type != OT_REG_INDEX)
+ return FALSE;
- /* We must not dabble with instructions in a ".set norerorder" block. */
- if (mips_opts.noreorder)
- return APPEND_ADD;
+ if (!match_regno (arg, OP_REG_GP, arg->token->u.regno, ®no))
+ return FALSE;
- /* Otherwise, it's our responsibility to fill branch delay slots. */
- if (delayed_branch_p (ip))
- {
- if (!branch_likely_p (ip) && can_swap_branch_p (ip))
- return APPEND_SWAP;
+ insn_insert_operand (arg->insn, operand, regno);
+ ++arg->token;
+ return TRUE;
+}
- pinfo = ip->insn_mo->pinfo;
- if (mips_opts.mips16
- && ISA_SUPPORTS_MIPS16E
- && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
- return APPEND_ADD_COMPACT;
+/* OP_PC matcher. */
- return APPEND_ADD_WITH_NOP;
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
+{
+ if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
+ {
+ ++arg->token;
+ return TRUE;
}
+ return FALSE;
+}
- return APPEND_ADD;
+/* OP_NON_ZERO_REG matcher. */
+
+static bfd_boolean
+match_non_zero_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (!match_reg (arg, OP_REG_GP, ®no))
+ return FALSE;
+
+ if (regno == 0)
+ return FALSE;
+
+ arg->last_regno = regno;
+ insn_insert_operand (arg->insn, operand, regno);
+ return TRUE;
}
-/* IP is a MIPS16 instruction whose opcode we have just changed.
- Point IP->insn_mo to the new opcode's definition. */
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the
+ register that we need to match. */
-static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
{
- const struct mips_opcode *mo, *end;
+ unsigned int regno;
- end = &mips16_opcodes[bfd_mips16_num_opcodes];
- for (mo = ip->insn_mo; mo < end; mo++)
- if ((ip->insn_opcode & mo->mask) == mo->match)
- {
- ip->insn_mo = mo;
- return;
- }
- abort ();
+ return match_reg (arg, OP_REG_GP, ®no) && regno == other_regno;
}
-/* For microMIPS macros, we need to generate a local number label
- as the target of branches. */
-#define MICROMIPS_LABEL_CHAR '\037'
-static unsigned long micromips_target_label;
-static char micromips_target_name[32];
+/* Read a floating-point constant from S for LI.S or LI.D. LENGTH is
+ the length of the value in bytes (4 for float, 8 for double) and
+ USING_GPRS says whether the destination is a GPR rather than an FPR.
-static char *
-micromips_label_name (void)
+ 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 = micromips_target_name;
- char symbol_name_temporary[24];
- unsigned long l;
- int i;
+ 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;
+ }
- if (*p)
- return p;
+ gas_assert (arg->token->u.flt.length == length);
+ data = arg->token->u.flt.data;
+ ++arg->token;
- i = 0;
- l = micromips_target_label;
-#ifdef LOCAL_LABEL_PREFIX
- *p++ = LOCAL_LABEL_PREFIX;
-#endif
- *p++ = 'L';
- *p++ = MICROMIPS_LABEL_CHAR;
- do
+ /* 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)))
{
- symbol_name_temporary[i++] = l % 10 + '0';
- l /= 10;
+ 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;
}
- while (l != 0);
- while (i > 0)
- *p++ = symbol_name_temporary[--i];
- *p = '\0';
- return micromips_target_name;
-}
+ /* 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 or MTHC1 is available.
+ Unlike most tests for 32-bit floating-point registers this check
+ specifically looks for GPR_SIZE == 32 as the FPXX ABI does not
+ permit 64-bit moves without MXHC1.
+ Force the constant into memory otherwise. */
+ && (using_gprs
+ || GPR_SIZE == 64
+ || ISA_HAS_MXHC1 (mips_opts.isa)
+ || FPR_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 (GPR_SIZE == 32 || (!using_gprs && 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;
+ }
-static void
-micromips_label_expr (expressionS *label_expr)
-{
- label_expr->X_op = O_symbol;
- label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
- label_expr->X_add_number = 0;
-}
+ /* 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";
+ }
-static void
-micromips_label_inc (void)
-{
- micromips_target_label++;
- *micromips_target_name = '\0';
+ 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;
}
-static void
-micromips_add_label (void)
+/* 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)
{
- symbolS *s;
+ unsigned int uval;
- s = colon (micromips_label_name ());
- micromips_label_inc ();
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
- if (IS_ELF)
- S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
-#else
- (void) s;
-#endif
-}
+ /* 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);
-/* If assembling microMIPS code, then return the microMIPS reloc
- corresponding to the requested one if any. Otherwise return
- the reloc unchanged. */
+ /* The suffix can be omitted when it is already part of the opcode. */
+ if (arg->token->type != OT_CHANNELS)
+ return match_p;
-static bfd_reloc_code_real_type
-micromips_map_reloc (bfd_reloc_code_real_type reloc)
-{
- static const bfd_reloc_code_real_type relocs[][2] =
+ uval = arg->token->u.channels;
+ if (operand->size == 2)
{
- /* Keep sorted incrementally by the left-hand key. */
- { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
- { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
- { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
- { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
- { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
- { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
- { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
- { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
- { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
- { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
- { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
- { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
- { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
- { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
- { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
- { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
- { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
- { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
- { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
- { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
- { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
- { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
- { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
- { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
- { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
- { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
- { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
- };
- bfd_reloc_code_real_type r;
- size_t i;
-
- if (!mips_opts.micromips)
- return reloc;
- for (i = 0; i < ARRAY_SIZE (relocs); i++)
- {
- r = relocs[i][0];
- if (r > reloc)
- return reloc;
- if (r == reloc)
- return relocs[i][1];
+ /* 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);
}
- return reloc;
-}
-/* Output an instruction. IP is the instruction information.
- ADDRESS_EXPR is an operand of the instruction to be used with
- RELOC_TYPE. EXPANSIONP is true if the instruction is part of
- a macro expansion. */
+ if (match_p && insn_extract_operand (arg->insn, operand) != uval)
+ return FALSE;
-static void
-append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
-{
- unsigned long prev_pinfo2, pinfo;
- bfd_boolean relaxed_branch = FALSE;
- enum append_method method;
- bfd_boolean relax32;
- int branch_disp;
+ ++arg->token;
+ if (!match_p)
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
- if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
- fix_loongson2f (ip);
+/* 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. */
- mips_mark_labels ();
+static bfd_boolean
+match_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ switch (operand->type)
+ {
+ case OP_INT:
+ return match_int_operand (arg, operand);
- file_ase_mips16 |= mips_opts.mips16;
- file_ase_micromips |= mips_opts.micromips;
+ case OP_MAPPED_INT:
+ return match_mapped_int_operand (arg, operand);
- prev_pinfo2 = history[0].insn_mo->pinfo2;
- pinfo = ip->insn_mo->pinfo;
+ case OP_MSB:
+ return match_msb_operand (arg, operand);
- if (mips_opts.micromips
- && !expansionp
- && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
- && micromips_insn_length (ip->insn_mo) != 2)
- || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
- && micromips_insn_length (ip->insn_mo) != 4)))
- as_warn (_("Wrong size instruction in a %u-bit branch delay slot"),
- (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ return match_reg_operand (arg, operand);
- if (address_expr == NULL)
- ip->complete_p = 1;
- else if (*reloc_type <= BFD_RELOC_UNUSED
- && address_expr->X_op == O_constant)
- {
- unsigned int tmp;
+ case OP_REG_PAIR:
+ return match_reg_pair_operand (arg, operand);
- ip->complete_p = 1;
- switch (*reloc_type)
- {
- case BFD_RELOC_32:
- ip->insn_opcode |= address_expr->X_add_number;
- break;
+ case OP_PCREL:
+ return match_pcrel_operand (arg);
- case BFD_RELOC_MIPS_HIGHEST:
- tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
- ip->insn_opcode |= tmp & 0xffff;
- break;
+ case OP_PERF_REG:
+ return match_perf_reg_operand (arg, operand);
- case BFD_RELOC_MIPS_HIGHER:
- tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
- ip->insn_opcode |= tmp & 0xffff;
- break;
+ case OP_ADDIUSP_INT:
+ return match_addiusp_operand (arg, operand);
- case BFD_RELOC_HI16_S:
- tmp = (address_expr->X_add_number + 0x8000) >> 16;
- ip->insn_opcode |= tmp & 0xffff;
- break;
+ case OP_CLO_CLZ_DEST:
+ return match_clo_clz_dest_operand (arg, operand);
- case BFD_RELOC_HI16:
- ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
- break;
+ case OP_LWM_SWM_LIST:
+ return match_lwm_swm_list_operand (arg, operand);
- case BFD_RELOC_UNUSED:
- case BFD_RELOC_LO16:
- case BFD_RELOC_MIPS_GOT_DISP:
- ip->insn_opcode |= address_expr->X_add_number & 0xffff;
- break;
+ case OP_ENTRY_EXIT_LIST:
+ return match_entry_exit_operand (arg, operand);
- case BFD_RELOC_MIPS_JMP:
- {
- int shift;
+ case OP_SAVE_RESTORE_LIST:
+ return match_save_restore_list_operand (arg);
- shift = mips_opts.micromips ? 1 : 2;
- if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
- as_bad (_("jump to misaligned address (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- ip->insn_opcode |= ((address_expr->X_add_number >> shift)
- & 0x3ffffff);
- ip->complete_p = 0;
- }
- break;
+ case OP_MDMX_IMM_REG:
+ return match_mdmx_imm_reg_operand (arg, operand);
- case BFD_RELOC_MIPS16_JMP:
- if ((address_expr->X_add_number & 3) != 0)
- as_bad (_("jump to misaligned address (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- ip->insn_opcode |=
- (((address_expr->X_add_number & 0x7c0000) << 3)
- | ((address_expr->X_add_number & 0xf800000) >> 7)
- | ((address_expr->X_add_number & 0x3fffc) >> 2));
- ip->complete_p = 0;
- break;
+ case OP_REPEAT_DEST_REG:
+ return match_tied_reg_operand (arg, arg->dest_regno);
- case BFD_RELOC_16_PCREL_S2:
- {
- int shift;
+ case OP_REPEAT_PREV_REG:
+ return match_tied_reg_operand (arg, arg->last_regno);
- shift = mips_opts.micromips ? 1 : 2;
- if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
- as_bad (_("branch to misaligned address (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- if (!mips_relax_branch)
- {
- if ((address_expr->X_add_number + (1 << (shift + 15)))
- & ~((1 << (shift + 16)) - 1))
- as_bad (_("branch address range overflow (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- ip->insn_opcode |= ((address_expr->X_add_number >> shift)
- & 0xffff);
- }
- ip->complete_p = 0;
- }
- break;
+ case OP_PC:
+ return match_pc_operand (arg);
- default:
- internalError ();
- }
- }
+ case OP_VU0_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, FALSE);
- if (mips_relax.sequence != 2 && !mips_opts.noreorder)
- {
- /* There are a lot of optimizations we could do that we don't.
- In particular, we do not, in general, reorder instructions.
- If you use gcc with optimization, it will reorder
- instructions and generally do much more optimization then we
- do here; repeating all that work in the assembler would only
- benefit hand written assembly code, and does not seem worth
- it. */
- int nops = (mips_optimize == 0
- ? nops_for_insn (0, history, NULL)
- : nops_for_insn_or_target (0, history, ip));
- if (nops > 0)
- {
- fragS *old_frag;
- unsigned long old_frag_offset;
- int i;
+ case OP_VU0_MATCH_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, TRUE);
- old_frag = frag_now;
- old_frag_offset = frag_now_fix ();
+ case OP_IMM_INDEX:
+ return match_imm_index_operand (arg, operand);
- for (i = 0; i < nops; i++)
- add_fixed_insn (NOP_INSN);
- insert_into_history (0, nops, NOP_INSN);
+ case OP_REG_INDEX:
+ return match_reg_index_operand (arg, operand);
- if (listing)
- {
- listing_prev_line ();
- /* We may be at the start of a variant frag. In case we
- are, make sure there is enough space for the frag
- after the frags created by listing_prev_line. The
- argument to frag_grow here must be at least as large
- as the argument to all other calls to frag_grow in
- this file. We don't have to worry about being in the
- middle of a variant frag, because the variants insert
- all needed nop instructions themselves. */
- frag_grow (40);
- }
+ case OP_SAME_RS_RT:
+ return match_same_rs_rt_operand (arg, operand);
- mips_move_labels ();
+ case OP_CHECK_PREV:
+ return match_check_prev_operand (arg, operand);
-#ifndef NO_ECOFF_DEBUGGING
- if (ECOFF_DEBUGGING)
- ecoff_fix_loc (old_frag, old_frag_offset);
-#endif
- }
+ case OP_NON_ZERO_REG:
+ return match_non_zero_reg_operand (arg, operand);
}
- else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
- {
- int nops;
-
- /* Work out how many nops in prev_nop_frag are needed by IP,
- ignoring hazards generated by the first prev_nop_frag_since
- instructions. */
- nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
- gas_assert (nops <= prev_nop_frag_holds);
+ abort ();
+}
- /* Enforce NOPS as a minimum. */
- if (nops > prev_nop_frag_required)
- prev_nop_frag_required = nops;
+/* ARG is the state after successfully matching an instruction.
+ Issue any queued-up warnings. */
- if (prev_nop_frag_holds == prev_nop_frag_required)
- {
- /* Settle for the current number of nops. Update the history
- accordingly (for the benefit of any future .set reorder code). */
- prev_nop_frag = NULL;
- insert_into_history (prev_nop_frag_since,
- prev_nop_frag_holds, NOP_INSN);
- }
+static void
+check_completed_insn (struct mips_arg_info *arg)
+{
+ if (arg->seen_at)
+ {
+ if (AT == ATREG)
+ as_warn (_("used $at without \".set noat\""));
else
- {
- /* Allow this instruction to replace one of the nops that was
- tentatively added to prev_nop_frag. */
- prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
- prev_nop_frag_holds--;
- prev_nop_frag_since++;
- }
+ as_warn (_("used $%u with \".set at=$%u\""), AT, AT);
}
+}
- method = get_append_method (ip);
- branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
+/* Return true if modifying general-purpose register REG needs a delay. */
-#ifdef OBJ_ELF
- /* The value passed to dwarf2_emit_insn is the distance between
- the beginning of the current instruction and the address that
- should be recorded in the debug tables. This is normally the
- current address.
-
- For MIPS16/microMIPS debug info we want to use ISA-encoded
- addresses, so we use -1 for an address higher by one than the
- current one.
-
- If the instruction produced is a branch that we will swap with
- the preceding instruction, then we add the displacement by which
- the branch will be moved backwards. This is more appropriate
- and for MIPS16/microMIPS code also prevents a debugger from
- placing a breakpoint in the middle of the branch (and corrupting
- code if software breakpoints are used). */
- dwarf2_emit_insn ((HAVE_CODE_COMPRESSION ? -1 : 0) + branch_disp);
-#endif
+static bfd_boolean
+reg_needs_delay (unsigned int reg)
+{
+ unsigned long prev_pinfo;
- relax32 = (mips_relax_branch
- /* Don't try branch relaxation within .set nomacro, or within
- .set noat if we use $at for PIC computations. If it turns
- out that the branch was out-of-range, we'll get an error. */
- && !mips_opts.warn_about_macros
- && (mips_opts.at || mips_pic == NO_PIC)
- /* Don't relax BPOSGE32/64 as they have no complementing
- branches. */
- && !(ip->insn_mo->membership & (INSN_DSP64 | INSN_DSP)));
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (!mips_opts.noreorder
+ && (((prev_pinfo & INSN_LOAD_MEMORY) && !gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC) && !cop_interlocks))
+ && (gpr_write_mask (&history[0]) & (1 << reg)))
+ return TRUE;
- if (!HAVE_CODE_COMPRESSION
- && address_expr
- && relax32
- && *reloc_type == BFD_RELOC_16_PCREL_S2
- && delayed_branch_p (ip))
- {
- relaxed_branch = TRUE;
- add_relaxed_insn (ip, (relaxed_branch_length
- (NULL, NULL,
- uncond_branch_p (ip) ? -1
- : branch_likely_p (ip) ? 1
- : 0)), 4,
- RELAX_BRANCH_ENCODE
- (AT,
- uncond_branch_p (ip),
- branch_likely_p (ip),
- pinfo & INSN_WRITE_GPR_31,
- 0),
- address_expr->X_add_symbol,
- address_expr->X_add_number);
- *reloc_type = BFD_RELOC_UNUSED;
- }
- else if (mips_opts.micromips
- && address_expr
- && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
- || *reloc_type > BFD_RELOC_UNUSED)
- && (delayed_branch_p (ip) || compact_branch_p (ip))
- /* Don't try branch relaxation when users specify
- 16-bit/32-bit instructions. */
- && !forced_insn_length)
- {
- bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
- int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
- int uncond = uncond_branch_p (ip) ? -1 : 0;
- int compact = compact_branch_p (ip);
- int al = pinfo & INSN_WRITE_GPR_31;
- int length32;
+ return FALSE;
+}
- gas_assert (address_expr != NULL);
- gas_assert (!mips_relax.sequence);
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+ Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+ by VR4120 errata. */
- relaxed_branch = TRUE;
- length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
- add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
- RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
- relax32, 0, 0),
- address_expr->X_add_symbol,
- address_expr->X_add_number);
- *reloc_type = BFD_RELOC_UNUSED;
- }
- else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
- {
- /* We need to set up a variant frag. */
- gas_assert (address_expr != NULL);
- add_relaxed_insn (ip, 4, 0,
- RELAX_MIPS16_ENCODE
- (*reloc_type - BFD_RELOC_UNUSED,
- forced_insn_length == 2, forced_insn_length == 4,
- delayed_branch_p (&history[0]),
- history[0].mips16_absolute_jump_p),
- make_expr_symbol (address_expr), 0);
- }
- else if (mips_opts.mips16
- && ! ip->use_extend
- && *reloc_type != BFD_RELOC_MIPS16_JMP)
+static unsigned int
+classify_vr4120_insn (const char *name)
+{
+ if (strncmp (name, "macc", 4) == 0)
+ return FIX_VR4120_MACC;
+ if (strncmp (name, "dmacc", 5) == 0)
+ return FIX_VR4120_DMACC;
+ if (strncmp (name, "mult", 4) == 0)
+ return FIX_VR4120_MULT;
+ if (strncmp (name, "dmult", 5) == 0)
+ return FIX_VR4120_DMULT;
+ if (strstr (name, "div"))
+ return FIX_VR4120_DIV;
+ if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
+ return FIX_VR4120_MTHILO;
+ return NUM_FIX_VR4120_CLASSES;
+}
+
+#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
+ for any INSN2 if INSN2 is null. */
+
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+ const struct mips_cl_insn *insn2)
+{
+ unsigned long pinfo1, pinfo2;
+ unsigned int mask;
+
+ /* 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;
+
+ /* 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 (!delayed_branch_p (ip))
- /* Make sure there is enough room to swap this instruction with
- a following jump instruction. */
- frag_grow (6);
- add_fixed_insn (ip);
+ if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+ return 2;
+ if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+ return 2;
}
- else
- {
- if (mips_opts.mips16
- && mips_opts.noreorder
- && delayed_branch_p (&history[0]))
- as_warn (_("extended instruction in delay slot"));
- if (mips_relax.sequence)
- {
- /* If we've reached the end of this frag, turn it into a variant
- frag and record the information for the instructions we've
- written so far. */
- if (frag_room () < 4)
- relax_close_frag ();
- mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
- }
+ /* If we're working around r7000 errata, there must be two instructions
+ between an mfhi or mflo and any instruction that uses the result. */
+ if (mips_7000_hilo_fix
+ && !mips_opts.micromips
+ && MF_HILO_INSN (pinfo1)
+ && (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1))))
+ return 2;
- if (mips_relax.sequence != 2)
- {
- if (mips_macro_warning.first_insn_sizes[0] == 0)
- mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
- mips_macro_warning.sizes[0] += insn_length (ip);
- mips_macro_warning.insns[0]++;
- }
- if (mips_relax.sequence != 1)
+ /* If we're working around 24K errata, one instruction is required
+ if an ERET or DERET is followed by a branch instruction. */
+ if (mips_fix_24k && !mips_opts.micromips)
+ {
+ if (insn1->insn_opcode == INSN_ERET
+ || insn1->insn_opcode == INSN_DERET)
{
- if (mips_macro_warning.first_insn_sizes[1] == 0)
- mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
- mips_macro_warning.sizes[1] += insn_length (ip);
- mips_macro_warning.insns[1]++;
+ if (insn2 == NULL
+ || insn2->insn_opcode == INSN_ERET
+ || insn2->insn_opcode == INSN_DERET
+ || delayed_branch_p (insn2))
+ return 1;
}
+ }
- if (mips_opts.mips16)
+ /* 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)
{
- ip->fixed_p = 1;
- ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
+ if (pinfo2 & INSN_LOAD_MEMORY)
+ return 3;
}
- add_fixed_insn (ip);
}
- if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
+ /* If working around VR4120 errata, check for combinations that need
+ a single intervening instruction. */
+ if (mips_fix_vr4120 && !mips_opts.micromips)
{
- bfd_reloc_code_real_type final_type[3];
- reloc_howto_type *howto0;
- reloc_howto_type *howto;
- int i;
-
- /* Perform any necessary conversion to microMIPS relocations
- and find out how many relocations there actually are. */
- for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
- final_type[i] = micromips_map_reloc (reloc_type[i]);
-
- /* In a compound relocation, it is the final (outermost)
- operator that determines the relocated field. */
- howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+ unsigned int class1, class2;
- if (howto == NULL)
+ class1 = classify_vr4120_insn (insn1->insn_mo->name);
+ if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
{
- /* To reproduce this failure try assembling gas/testsuites/
- gas/mips/mips16-intermix.s with a mips-ecoff targeted
- assembler. */
- as_bad (_("Unsupported MIPS relocation number %d"),
- final_type[i - 1]);
- howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+ if (insn2 == NULL)
+ return 1;
+ class2 = classify_vr4120_insn (insn2->insn_mo->name);
+ if (vr4120_conflicts[class1] & (1 << class2))
+ return 1;
}
+ }
- if (i > 1)
- howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
- ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
- bfd_get_reloc_size (howto),
- address_expr,
- howto0 && howto0->pc_relative,
- final_type[0]);
-
- /* Tag symbols that have a R_MIPS16_26 relocation against them. */
- if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
- *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
-
- /* These relocations can have an addend that won't fit in
- 4 octets for 64bit assembly. */
- if (HAVE_64BIT_GPRS
- && ! howto->partial_inplace
- && (reloc_type[0] == BFD_RELOC_16
- || reloc_type[0] == BFD_RELOC_32
- || reloc_type[0] == BFD_RELOC_MIPS_JMP
- || reloc_type[0] == BFD_RELOC_GPREL16
- || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
- || reloc_type[0] == BFD_RELOC_GPREL32
- || reloc_type[0] == BFD_RELOC_64
- || reloc_type[0] == BFD_RELOC_CTOR
- || reloc_type[0] == BFD_RELOC_MIPS_SUB
- || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
- || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
- || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
- || reloc_type[0] == BFD_RELOC_MIPS_REL16
- || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
- || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
- || hi16_reloc_p (reloc_type[0])
- || lo16_reloc_p (reloc_type[0])))
- ip->fixp[0]->fx_no_overflow = 1;
-
- if (mips_relax.sequence)
+ if (!HAVE_CODE_COMPRESSION)
+ {
+ /* Check for GPR or coprocessor load delays. All such delays
+ are on the RT register. */
+ /* Itbl support may require additional care here. */
+ if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY))
+ || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC)))
{
- if (mips_relax.first_fixup == 0)
- mips_relax.first_fixup = ip->fixp[0];
+ if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
+ return 1;
}
- else if (reloc_needs_lo_p (*reloc_type))
- {
- struct mips_hi_fixup *hi_fixup;
- /* Reuse the last entry if it already has a matching %lo. */
- hi_fixup = mips_hi_fixup_list;
- if (hi_fixup == 0
- || !fixup_has_matching_lo_p (hi_fixup->fixp))
+ /* Check for generic coprocessor hazards.
+
+ This case is not handled very well. There is no special
+ knowledge of CP0 handling, and the coprocessors other than
+ the floating point unit are not distinguished at all. */
+ /* Itbl support may require additional care here. FIXME!
+ Need to modify this to include knowledge about
+ user specified delays! */
+ else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE))
+ || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
+ {
+ /* Handle cases where INSN1 writes to a known general coprocessor
+ register. There must be a one instruction delay before INSN2
+ if INSN2 reads that register, otherwise no delay is needed. */
+ mask = fpr_write_mask (insn1);
+ if (mask != 0)
{
- hi_fixup = ((struct mips_hi_fixup *)
- xmalloc (sizeof (struct mips_hi_fixup)));
- hi_fixup->next = mips_hi_fixup_list;
- mips_hi_fixup_list = hi_fixup;
+ if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
+ return 1;
}
- hi_fixup->fixp = ip->fixp[0];
- hi_fixup->seg = now_seg;
- }
+ else
+ {
+ /* Read-after-write dependencies on the control registers
+ require a two-instruction gap. */
+ if ((pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 2;
- /* Add fixups for the second and third relocations, if given.
- Note that the ABI allows the second relocation to be
- against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
- moment we only use RSS_UNDEF, but we could add support
- for the others if it ever becomes necessary. */
- for (i = 1; i < 3; i++)
- if (reloc_type[i] != BFD_RELOC_UNUSED)
- {
- ip->fixp[i] = fix_new (ip->frag, ip->where,
- ip->fixp[0]->fx_size, NULL, 0,
- FALSE, final_type[i]);
+ /* We don't know exactly what INSN1 does. If INSN2 is
+ also a coprocessor instruction, assume there must be
+ a one instruction gap. */
+ if (pinfo2 & INSN_COP)
+ return 1;
+ }
+ }
- /* Use fx_tcbit to mark compound relocs. */
- ip->fixp[0]->fx_tcbit = 1;
- ip->fixp[i]->fx_tcbit = 1;
- }
+ /* Check for read-after-write dependencies on the coprocessor
+ control registers in cases where INSN1 does not need a general
+ coprocessor delay. This means that INSN1 is a floating point
+ comparison instruction. */
+ /* Itbl support may require additional care here. */
+ else if (!cop_interlocks
+ && (pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 1;
}
- install_insn (ip);
-
- /* Update the register mask information. */
- mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
- mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
- switch (method)
- {
- case APPEND_ADD:
- insert_into_history (0, 1, ip);
- break;
+ /* Forbidden slots can not contain Control Transfer Instructions (CTIs)
+ CTIs include all branches and jumps, nal, eret, eretnc, deret, wait
+ and pause. */
+ if ((insn1->insn_mo->pinfo2 & INSN2_FORBIDDEN_SLOT)
+ && ((pinfo2 & INSN_NO_DELAY_SLOT)
+ || (insn2 && delayed_branch_p (insn2))))
+ return 1;
- case APPEND_ADD_WITH_NOP:
- {
- struct mips_cl_insn *nop;
-
- insert_into_history (0, 1, ip);
- nop = get_delay_slot_nop (ip);
- add_fixed_insn (nop);
- insert_into_history (0, 1, nop);
- if (mips_relax.sequence)
- mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
- }
- break;
+ return 0;
+}
- case APPEND_ADD_COMPACT:
- /* Convert MIPS16 jr/jalr into a "compact" jump. */
- gas_assert (mips_opts.mips16);
- ip->insn_opcode |= 0x0080;
- find_altered_mips16_opcode (ip);
- install_insn (ip);
- insert_into_history (0, 1, ip);
- break;
+/* Return the number of nops that would be needed to work around the
+ VR4130 mflo/mfhi errata if instruction INSN immediately followed
+ the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
+ that are contained within the first IGNORE instructions of HIST. */
- case APPEND_SWAP:
- {
- struct mips_cl_insn delay = history[0];
- if (mips_opts.mips16)
- {
- know (delay.frag == ip->frag);
- move_insn (ip, delay.frag, delay.where);
- move_insn (&delay, ip->frag, ip->where + insn_length (ip));
- }
- else if (relaxed_branch)
- {
- /* Add the delay slot instruction to the end of the
- current frag and shrink the fixed part of the
- original frag. If the branch occupies the tail of
- the latter, move it backwards to cover the gap. */
- delay.frag->fr_fix -= branch_disp;
- if (delay.frag == ip->frag)
- move_insn (ip, ip->frag, ip->where - branch_disp);
- add_fixed_insn (&delay);
- }
- else
- {
- move_insn (&delay, ip->frag,
- ip->where - branch_disp + insn_length (ip));
- move_insn (ip, history[0].frag, history[0].where);
- }
- history[0] = *ip;
- delay.fixed_p = 1;
- insert_into_history (0, 1, &delay);
- }
- break;
- }
+static int
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int i, j;
+ unsigned int mask;
- /* If we have just completed an unconditional branch, clear the history. */
- if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
- || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
- mips_no_prev_insn ();
+ /* Check if the instruction writes to HI or LO. MTHI and MTLO
+ are not affected by the errata. */
+ if (insn != 0
+ && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+ || strcmp (insn->insn_mo->name, "mtlo") == 0
+ || strcmp (insn->insn_mo->name, "mthi") == 0))
+ return 0;
- /* We need to emit a label at the end of branch-likely macros. */
- if (emit_branch_likely_macro)
- {
- emit_branch_likely_macro = FALSE;
- micromips_add_label ();
- }
+ /* Search for the first MFLO or MFHI. */
+ for (i = 0; i < MAX_VR4130_NOPS; i++)
+ if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
+ {
+ /* Extract the destination register. */
+ mask = gpr_write_mask (&hist[i]);
- /* We just output an insn, so the next one doesn't have a label. */
- mips_clear_insn_labels ();
-}
+ /* No nops are needed if INSN reads that register. */
+ if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
+ return 0;
-/* Forget that there was any previous instruction or label. */
+ /* ...or if any of the intervening instructions do. */
+ for (j = 0; j < i; j++)
+ if (gpr_read_mask (&hist[j]) & mask)
+ return 0;
-static void
-mips_no_prev_insn (void)
-{
- prev_nop_frag = NULL;
- insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
- mips_clear_insn_labels ();
+ if (i >= ignore)
+ return MAX_VR4130_NOPS - i;
+ }
+ return 0;
}
-/* This function must be called before we emit something other than
- instructions. It is like mips_no_prev_insn except that it inserts
- any NOPS that might be needed by previous instructions. */
-
-void
-mips_emit_delays (void)
-{
- if (! mips_opts.noreorder)
- {
- int nops = nops_for_insn (0, history, NULL);
- if (nops > 0)
- {
- while (nops-- > 0)
- add_fixed_insn (NOP_INSN);
- mips_move_labels ();
- }
- }
- mips_no_prev_insn ();
-}
+#define BASE_REG_EQ(INSN1, INSN2) \
+ ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+ == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
-/* Start a (possibly nested) noreorder block. */
+/* Return the minimum alignment for this store instruction. */
-static void
-start_noreorder (void)
+static int
+fix_24k_align_to (const struct mips_opcode *mo)
{
- if (mips_opts.noreorder == 0)
- {
- unsigned int i;
- int nops;
-
- /* None of the instructions before the .set noreorder can be moved. */
- for (i = 0; i < ARRAY_SIZE (history); i++)
- history[i].fixed_p = 1;
+ if (strcmp (mo->name, "sh") == 0)
+ return 2;
- /* Insert any nops that might be needed between the .set noreorder
- block and the previous instructions. We will later remove any
- nops that turn out not to be needed. */
- nops = nops_for_insn (0, history, NULL);
- if (nops > 0)
- {
- if (mips_optimize != 0)
- {
- /* Record the frag which holds the nop instructions, so
- that we can remove them if we don't need them. */
- frag_grow (nops * NOP_INSN_SIZE);
- prev_nop_frag = frag_now;
- prev_nop_frag_holds = nops;
- prev_nop_frag_required = 0;
- prev_nop_frag_since = 0;
- }
+ if (strcmp (mo->name, "swc1") == 0
+ || strcmp (mo->name, "swc2") == 0
+ || strcmp (mo->name, "sw") == 0
+ || strcmp (mo->name, "sc") == 0
+ || strcmp (mo->name, "s.s") == 0)
+ return 4;
- for (; nops > 0; --nops)
- add_fixed_insn (NOP_INSN);
+ if (strcmp (mo->name, "sdc1") == 0
+ || strcmp (mo->name, "sdc2") == 0
+ || strcmp (mo->name, "s.d") == 0)
+ return 8;
- /* Move on to a new frag, so that it is safe to simply
- decrease the size of prev_nop_frag. */
- frag_wane (frag_now);
- frag_new (0);
- mips_move_labels ();
- }
- mips_mark_labels ();
- mips_clear_insn_labels ();
- }
- mips_opts.noreorder++;
- mips_any_noreorder = 1;
+ /* sb, swl, swr */
+ return 1;
}
-/* End a nested noreorder block. */
-
-static void
-end_noreorder (void)
-{
- mips_opts.noreorder--;
- if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
- {
- /* Commit to inserting prev_nop_frag_required nops and go back to
- handling nop insertion the .set reorder way. */
- prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
- * NOP_INSN_SIZE);
- insert_into_history (prev_nop_frag_since,
- prev_nop_frag_required, NOP_INSN);
- prev_nop_frag = NULL;
- }
-}
+struct fix_24k_store_info
+ {
+ /* Immediate offset, if any, for this store instruction. */
+ short off;
+ /* Alignment required by this store instruction. */
+ int align_to;
+ /* True for register offsets. */
+ int register_offset;
+ };
-/* Set up global variables for the start of a new macro. */
+/* Comparison function used by qsort. */
-static void
-macro_start (void)
+static int
+fix_24k_sort (const void *a, const void *b)
{
- 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;
+ const struct fix_24k_store_info *pos1 = a;
+ const struct fix_24k_store_info *pos2 = b;
+
+ return (pos1->off - pos2->off);
}
-/* 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. */
+/* INSN is a store instruction. Try to record the store information
+ in STINFO. Return false if the information isn't known. */
-static const char *
-macro_warning (relax_substateT subtype)
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+ const struct mips_cl_insn *insn)
{
- 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;
+ /* The instruction must have a known offset. */
+ if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+ return FALSE;
+
+ stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+ stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+ return TRUE;
}
-/* Finish up a macro. Emit warnings as appropriate. */
+/* Return the number of nops that would be needed to work around the 24k
+ "lost data on stores during refill" errata if instruction INSN
+ immediately followed the 2 instructions described by HIST.
+ Ignore hazards that are contained within the first IGNORE
+ instructions of HIST.
-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)
- {
- 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;
- }
-
- /* 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;
- }
+ Problem: The FSB (fetch store buffer) acts as an intermediate buffer
+ for the data cache refills and store data. The following describes
+ the scenario where the store data could be lost.
- /* 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;
+ * A data cache miss, due to either a load or a store, causing fill
+ data to be supplied by the memory subsystem
+ * The first three doublewords of fill data are returned and written
+ into the cache
+ * A sequence of four stores occurs in consecutive cycles around the
+ final doubleword of the fill:
+ * Store A
+ * Store B
+ * Store C
+ * Zero, One or more instructions
+ * Store D
- 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;
- }
+ The four stores A-D must be to different doublewords of the line that
+ is being filled. The fourth instruction in the sequence above permits
+ the fill of the final doubleword to be transferred from the FSB into
+ the cache. In the sequence above, the stores may be either integer
+ (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
+ swxc1, sdxc1, suxc1) stores, as long as the four stores are to
+ different doublewords on the line. If the floating point unit is
+ running in 1:2 mode, it is not possible to create the sequence above
+ using only floating point store instructions.
- /* 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;
+ In this case, the cache line being filled is incorrectly marked
+ invalid, thereby losing the data from any store to the line that
+ occurs between the original miss and the completion of the five
+ cycle sequence shown above.
- s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
- msg = macro_warning (s);
- if (msg != NULL)
- as_warn ("%s", msg);
- subtype &= ~s;
- }
+ The workarounds are:
- /* 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;
-}
+ * Run the data cache in write-through mode.
+ * Insert a non-store instruction between
+ Store A and Store B or Store B and Store C. */
-/* Instruction operand formats used in macros that vary between
- standard MIPS and microMIPS code. */
+static int
+nops_for_24k (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ struct fix_24k_store_info pos[3];
+ int align, i, base_offset;
-static const char * const brk_fmt[2] = { "c", "mF" };
-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] = { "d", "mj" };
-static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
-static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
+ if (ignore >= 2)
+ return 0;
-#define BRK_FMT (brk_fmt[mips_opts.micromips])
-#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])
-#define SHFT_FMT (shft_fmt[mips_opts.micromips])
-#define TRAP_FMT (trap_fmt[mips_opts.micromips])
+ /* If the previous instruction wasn't a store, there's nothing to
+ worry about. */
+ if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
-/* 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. */
+ /* If the instructions after the previous one are unknown, we have
+ to assume the worst. */
+ if (!insn)
+ return 1;
-static void
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
-{
- int i, next;
+ /* Check whether we are dealing with three consecutive stores. */
+ if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+ || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
- 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);
-}
+ /* If we don't know the relationship between the store addresses,
+ assume the worst. */
+ if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
+ || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
+ return 1;
-/* 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 (!fix_24k_record_store_info (&pos[0], insn)
+ || !fix_24k_record_store_info (&pos[1], &hist[0])
+ || !fix_24k_record_store_info (&pos[2], &hist[1]))
+ return 1;
-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;
- struct hash_control *hash;
- struct mips_cl_insn insn;
- va_list args;
+ qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
- va_start (args, fmt);
+ /* Pick a value of ALIGN and X such that all offsets are adjusted by
+ X bytes and such that the base register + X is known to be aligned
+ to align bytes. */
- if (mips_opts.mips16)
+ if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+ align = 8;
+ else
{
- mips16_macro_build (ep, name, fmt, &args);
- va_end (args);
- return;
+ align = pos[0].align_to;
+ base_offset = pos[0].off;
+ for (i = 1; i < 3; i++)
+ if (align < pos[i].align_to)
+ {
+ align = pos[i].align_to;
+ base_offset = pos[i].off;
+ }
+ for (i = 0; i < 3; i++)
+ pos[i].off -= base_offset;
}
- 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);
+ pos[0].off &= ~align + 1;
+ pos[1].off &= ~align + 1;
+ pos[2].off &= ~align + 1;
- do
- {
- /* Search until we get a match for NAME. It is assumed here that
- macros will never generate MDMX, MIPS-3D, or MT instructions.
- We try to match an instruction that fulfils the branch delay
- slot instruction length requirement (if any) of the previous
- instruction. While doing this we record the first instruction
- seen that matches all the other conditions and use it anyway
- if the requirement cannot be met; we will issue an appropriate
- warning later on. */
- if (strcmp (fmt, amo->args) == 0
- && amo->pinfo != INSN_MACRO
- && is_opcode_valid (amo)
- && is_size_valid (amo))
- {
- if (is_delay_slot_valid (amo))
- {
- mo = amo;
- break;
- }
- else if (!mo)
- mo = amo;
- }
+ /* If any two stores write to the same chunk, they also write to the
+ same doubleword. The offsets are still sorted at this point. */
+ if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+ return 0;
- ++amo;
- gas_assert (amo->name);
- }
- while (strcmp (name, amo->name) == 0);
+ /* A range of at least 9 bytes is needed for the stores to be in
+ non-overlapping doublewords. */
+ if (pos[2].off - pos[0].off <= 8)
+ return 0;
- gas_assert (mo);
- create_insn (&insn, mo);
- for (;;)
- {
- switch (*fmt++)
- {
- case '\0':
- break;
+ if (pos[2].off - pos[1].off >= 24
+ || pos[1].off - pos[0].off >= 24
+ || pos[2].off - pos[0].off >= 32)
+ return 0;
- case ',':
- case '(':
- case ')':
- continue;
+ return 1;
+}
- case '+':
- switch (*fmt++)
- {
- case 'A':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips,
- EXTLSB, insn, va_arg (args, int));
- continue;
+/* Return the number of nops that would be needed if instruction INSN
+ immediately followed the MAX_NOPS instructions given by HIST,
+ where HIST[0] is the most recent instruction. Ignore hazards
+ between INSN and the first IGNORE instructions in HIST.
- case 'B':
- case 'F':
- /* Note that in the macro case, these arguments are already
- in MSB form. (When handling the instruction in the
- non-macro case, these arguments are sizes from which
- MSB values must be calculated.) */
- INSERT_OPERAND (mips_opts.micromips,
- INSMSB, insn, va_arg (args, int));
- continue;
+ If INSN is null, return the worse-case number of nops for any
+ instruction. */
- case 'C':
- case 'G':
- case 'H':
- /* Note that in the macro case, these arguments are already
- in MSBD form. (When handling the instruction in the
- non-macro case, these arguments are sizes from which
- MSBD values must be calculated.) */
- INSERT_OPERAND (mips_opts.micromips,
- EXTMSBD, insn, va_arg (args, int));
- continue;
-
- case 'Q':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
- continue;
-
- default:
- internalError ();
- }
- continue;
+static int
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int i, nops, tmp_nops;
- case '2':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, BP, insn, va_arg (args, int));
- continue;
+ nops = 0;
+ for (i = ignore; i < MAX_DELAY_NOPS; i++)
+ {
+ tmp_nops = insns_between (hist + i, insn) - i;
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
- case 'n':
- gas_assert (mips_opts.micromips);
- case 't':
- case 'w':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips, RT, insn, va_arg (args, int));
- continue;
+ if (mips_fix_vr4130 && !mips_opts.micromips)
+ {
+ tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
- case 'c':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE, insn, va_arg (args, int));
- continue;
+ if (mips_fix_24k && !mips_opts.micromips)
+ {
+ tmp_nops = nops_for_24k (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
- case 'W':
- gas_assert (!mips_opts.micromips);
- case 'T':
- INSERT_OPERAND (mips_opts.micromips, FT, insn, va_arg (args, int));
- continue;
+ return nops;
+}
- case 'G':
- if (mips_opts.micromips)
- INSERT_OPERAND (1, RS, insn, va_arg (args, int));
- else
- INSERT_OPERAND (0, RD, insn, va_arg (args, int));
- continue;
+/* The variable arguments provide NUM_INSNS extra instructions that
+ might be added to HIST. Return the largest number of nops that
+ would be needed after the extended sequence, ignoring hazards
+ in the first IGNORE instructions. */
- case 'K':
- gas_assert (!mips_opts.micromips);
- case 'd':
- INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
- continue;
+static int
+nops_for_sequence (int num_insns, int ignore,
+ const struct mips_cl_insn *hist, ...)
+{
+ va_list args;
+ struct mips_cl_insn buffer[MAX_NOPS];
+ struct mips_cl_insn *cursor;
+ int nops;
- case 'U':
- gas_assert (!mips_opts.micromips);
- {
- int tmp = va_arg (args, int);
+ va_start (args, hist);
+ cursor = buffer + num_insns;
+ memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
+ while (cursor > buffer)
+ *--cursor = *va_arg (args, const struct mips_cl_insn *);
- INSERT_OPERAND (0, RT, insn, tmp);
- INSERT_OPERAND (0, RD, insn, tmp);
- }
- continue;
+ nops = nops_for_insn (ignore, buffer, NULL);
+ va_end (args);
+ return nops;
+}
- case 'V':
- case 'S':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FS, insn, va_arg (args, int));
- continue;
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+ worst-case delay for the branch target. */
- case 'z':
- continue;
+static int
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int nops, tmp_nops;
- case '<':
- INSERT_OPERAND (mips_opts.micromips,
- SHAMT, insn, va_arg (args, int));
- continue;
+ nops = nops_for_insn (ignore, hist, insn);
+ if (delayed_branch_p (insn))
+ {
+ tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+ hist, insn, get_delay_slot_nop (insn));
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+ else if (compact_branch_p (insn))
+ {
+ tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+ return nops;
+}
- case 'D':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FD, insn, va_arg (args, int));
- continue;
+/* Fix NOP issue: Replace nops by "or at,at,zero". */
- case 'B':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE20, insn, va_arg (args, int));
- continue;
+static void
+fix_loongson2f_nop (struct mips_cl_insn * ip)
+{
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "nop") == 0)
+ ip->insn_opcode = LOONGSON2F_NOP_INSN;
+}
- case 'J':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE19, insn, va_arg (args, int));
- continue;
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+ jr target pc &= 'hffff_ffff_cfff_ffff. */
- case 'q':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE2, insn, va_arg (args, int));
- continue;
+static void
+fix_loongson2f_jump (struct mips_cl_insn * ip)
+{
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "j") == 0
+ || strcmp (ip->insn_mo->name, "jr") == 0
+ || strcmp (ip->insn_mo->name, "jalr") == 0)
+ {
+ int sreg;
+ expressionS ep;
- case 'b':
- case 's':
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, insn, va_arg (args, int));
- continue;
+ if (! mips_opts.at)
+ return;
- case 'i':
- case 'j':
- macro_read_relocs (&args, r);
- gas_assert (*r == BFD_RELOC_GPREL16
- || *r == BFD_RELOC_MIPS_HIGHER
- || *r == BFD_RELOC_HI16_S
- || *r == BFD_RELOC_LO16
- || *r == BFD_RELOC_MIPS_GOT_OFST);
- continue;
+ sreg = EXTRACT_OPERAND (0, RS, *ip);
+ if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
+ return;
- case 'o':
- macro_read_relocs (&args, r);
- continue;
+ ep.X_op = O_constant;
+ ep.X_add_number = 0xcfff0000;
+ macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
+ ep.X_add_number = 0xffff;
+ macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
+ macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
+ }
+}
- case 'u':
- macro_read_relocs (&args, r);
- gas_assert (ep != NULL
- && (ep->X_op == O_constant
- || (ep->X_op == O_symbol
- && (*r == BFD_RELOC_MIPS_HIGHEST
- || *r == BFD_RELOC_HI16_S
- || *r == BFD_RELOC_HI16
- || *r == BFD_RELOC_GPREL16
- || *r == BFD_RELOC_MIPS_GOT_HI16
- || *r == BFD_RELOC_MIPS_CALL_HI16))));
- continue;
+static void
+fix_loongson2f (struct mips_cl_insn * ip)
+{
+ if (mips_fix_loongson2f_nop)
+ fix_loongson2f_nop (ip);
- case 'p':
- gas_assert (ep != NULL);
+ if (mips_fix_loongson2f_jump)
+ fix_loongson2f_jump (ip);
+}
- /*
- * 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);
+/* IP is a branch that has a delay slot, and we need to fill it
+ automatically. Return true if we can do that by swapping IP
+ with the previous instruction.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
- if ((ep->X_add_number & 3) != 0)
- as_bad (_("branch to misaligned address (0x%lx)"),
- (unsigned long) ep->X_add_number);
- if ((ep->X_add_number + 0x20000) & ~0x3ffff)
- as_bad (_("branch address range overflow (0x%lx)"),
- (unsigned long) ep->X_add_number);
- insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
- ep = NULL;
- }
- else
- *r = BFD_RELOC_16_PCREL_S2;
- continue;
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+ 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;
- case 'a':
- gas_assert (ep != NULL);
- *r = BFD_RELOC_MIPS_JMP;
- continue;
+ /* -O2 and above is required for this optimization. */
+ if (mips_optimize < 2)
+ return FALSE;
- case 'C':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
- continue;
+ /* If we have seen .set volatile or .set nomove, don't optimize. */
+ if (mips_opts.nomove)
+ return FALSE;
- case 'k':
- INSERT_OPERAND (mips_opts.micromips,
- CACHE, insn, va_arg (args, unsigned long));
- continue;
+ /* We can't swap if the previous instruction's position is fixed. */
+ if (history[0].fixed_p)
+ return FALSE;
- case '|':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, TRAP, insn, va_arg (args, int));
- continue;
+ /* If the previous previous insn was in a .set noreorder, we can't
+ swap. Actually, the MIPS assembler will swap in this situation.
+ However, gcc configured -with-gnu-as will generate code like
- case '.':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, OFFSET10, insn, va_arg (args, int));
- continue;
+ .set noreorder
+ lw $4,XXX
+ .set reorder
+ INSN
+ bne $4,$0,foo
- case '\\':
- INSERT_OPERAND (mips_opts.micromips,
- 3BITPOS, insn, va_arg (args, unsigned int));
- continue;
+ in which we can not swap the bne and INSN. If gcc is not configured
+ -with-gnu-as, it does not output the .set pseudo-ops. */
+ if (history[1].noreorder_p)
+ return FALSE;
- case '~':
- INSERT_OPERAND (mips_opts.micromips,
- OFFSET12, insn, va_arg (args, unsigned long));
- continue;
+ /* If the previous instruction had a fixup in mips16 mode, we can not swap.
+ This means that the previous instruction was a 4-byte one anyhow. */
+ if (mips_opts.mips16 && history[0].fixp[0])
+ return FALSE;
- case 'N':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, BCC, insn, va_arg (args, int));
- continue;
+ /* If the branch is itself the target of a branch, we can not swap.
+ We cheat on this; all we check for is whether there is a label on
+ this instruction. If there are any branches to anything other than
+ a label, users must use .set noreorder. */
+ if (seg_info (now_seg)->label_list)
+ return FALSE;
- case 'm': /* Opcode extension character. */
- gas_assert (mips_opts.micromips);
- switch (*fmt++)
- {
- case 'j':
- INSERT_OPERAND (1, MJ, insn, va_arg (args, int));
- break;
+ /* If the previous instruction is in a variant frag other than this
+ branch's one, we cannot do the swap. This does not apply to
+ MIPS16 code, which uses variant frags for different purposes. */
+ if (!mips_opts.mips16
+ && history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent)
+ return FALSE;
- case 'p':
- INSERT_OPERAND (1, MP, insn, va_arg (args, int));
- break;
+ /* We do not swap with instructions that cannot architecturally
+ be placed in a branch delay slot, such as SYNC or ERET. We
+ also refrain from swapping with a trap instruction, since it
+ complicates trap handlers to have the trap instruction be in
+ a delay slot. */
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (prev_pinfo & INSN_NO_DELAY_SLOT)
+ return FALSE;
- case 'F':
- INSERT_OPERAND (1, IMMF, insn, va_arg (args, int));
- break;
+ /* Check for conflicts between the branch and the instructions
+ before the candidate delay slot. */
+ if (nops_for_insn (0, history + 1, ip) > 0)
+ return FALSE;
- default:
- internalError ();
- }
- continue;
+ /* Check for conflicts between the swapped sequence and the
+ target of the branch. */
+ if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
+ return FALSE;
- default:
- internalError ();
- }
- break;
+ /* If the branch reads a register that the previous
+ instruction sets, we can not swap. */
+ gpr_read = gpr_read_mask (ip);
+ prev_gpr_write = gpr_write_mask (&history[0]);
+ if (gpr_read & prev_gpr_write)
+ return FALSE;
+
+ 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 (gpr_write & prev_gpr_write)
+ return FALSE;
+
+ /* If the branch writes a register that the previous
+ instruction reads, we can not swap. */
+ prev_gpr_read = gpr_read_mask (&history[0]);
+ if (gpr_write & prev_gpr_read)
+ return FALSE;
+
+ /* If one instruction sets a condition code and the
+ other one uses a condition code, we can not swap. */
+ pinfo = ip->insn_mo->pinfo;
+ if ((pinfo & INSN_READ_COND_CODE)
+ && (prev_pinfo & INSN_WRITE_COND_CODE))
+ return FALSE;
+ if ((pinfo & INSN_WRITE_COND_CODE)
+ && (prev_pinfo & INSN_READ_COND_CODE))
+ return FALSE;
+
+ /* If the previous instruction uses the PC, we can not swap. */
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ if (prev_pinfo2 & INSN2_READ_PC)
+ return FALSE;
+
+ /* If the previous instruction has an incorrect size for a fixed
+ branch delay slot in microMIPS mode, we cannot swap. */
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
+ && insn_length (history) != 2)
+ return FALSE;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
+ && insn_length (history) != 4)
+ return FALSE;
+
+ /* On R5900 short loops need to be fixed by inserting a nop in
+ the branch delay slots.
+ A short loop can be terminated too early. */
+ if (mips_opts.arch == CPU_R5900
+ /* Check if instruction has a parameter, ignore "j $31". */
+ && (address_expr != NULL)
+ /* Parameter must be 16 bit. */
+ && (*reloc_type == BFD_RELOC_16_PCREL_S2)
+ /* Branch to same segment. */
+ && (S_GET_SEGMENT (address_expr->X_add_symbol) == now_seg)
+ /* Branch to same code fragment. */
+ && (symbol_get_frag (address_expr->X_add_symbol) == frag_now)
+ /* Can only calculate branch offset if value is known. */
+ && symbol_constant_p (address_expr->X_add_symbol)
+ /* Check if branch is really conditional. */
+ && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ int distance;
+ /* Check if loop is shorter than 6 instructions including
+ branch and delay slot. */
+ distance = frag_now_fix () - S_GET_VALUE (address_expr->X_add_symbol);
+ if (distance <= 20)
+ {
+ int i;
+ int rv;
+
+ rv = FALSE;
+ /* When the loop includes branches or jumps,
+ it is not a short loop. */
+ for (i = 0; i < (distance / 4); i++)
+ {
+ if ((history[i].cleared_p)
+ || delayed_branch_p (&history[i]))
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ if (rv == FALSE)
+ {
+ /* Insert nop after branch to fix short loop. */
+ return FALSE;
+ }
+ }
}
- va_end (args);
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
- append_insn (&insn, ep, r, TRUE);
+ return TRUE;
}
-static void
-mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
- va_list *args)
+/* Decide how we should add IP to the instruction stream.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
+
+static enum append_method
+get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
{
- struct mips_opcode *mo;
- struct mips_cl_insn insn;
- bfd_reloc_code_real_type r[3]
- = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+ /* The relaxed version of a macro sequence must be inherently
+ hazard-free. */
+ if (mips_relax.sequence == 2)
+ return APPEND_ADD;
- mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
- gas_assert (mo);
- gas_assert (strcmp (name, mo->name) == 0);
+ /* We must not dabble with instructions in a ".set noreorder" block. */
+ if (mips_opts.noreorder)
+ return APPEND_ADD;
- while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
+ /* Otherwise, it's our responsibility to fill branch delay slots. */
+ if (delayed_branch_p (ip))
{
- ++mo;
- gas_assert (mo->name);
- gas_assert (strcmp (name, mo->name) == 0);
- }
+ if (!branch_likely_p (ip)
+ && can_swap_branch_p (ip, address_expr, reloc_type))
+ return APPEND_SWAP;
- create_insn (&insn, mo);
- for (;;)
- {
- int c;
+ if (mips_opts.mips16
+ && ISA_SUPPORTS_MIPS16E
+ && gpr_read_mask (ip) != 0)
+ return APPEND_ADD_COMPACT;
- c = *fmt++;
- switch (c)
- {
- case '\0':
- break;
+ if (mips_opts.micromips
+ && ((ip->insn_opcode & 0xffe0) == 0x4580
+ || (!forced_insn_length
+ && ((ip->insn_opcode & 0xfc00) == 0xcc00
+ || (ip->insn_opcode & 0xdc00) == 0x8c00))
+ || (ip->insn_opcode & 0xdfe00000) == 0x94000000
+ || (ip->insn_opcode & 0xdc1f0000) == 0x94000000))
+ return APPEND_ADD_COMPACT;
- case ',':
- case '(':
- case ')':
- continue;
+ return APPEND_ADD_WITH_NOP;
+ }
- case 'y':
- case 'w':
- MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
- continue;
+ return APPEND_ADD;
+}
- case 'x':
- case 'v':
- MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
- continue;
+/* IP is an instruction whose opcode we have just changed, END points
+ to the end of the opcode table processed. Point IP->insn_mo to the
+ new opcode's definition. */
- case 'z':
- MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
- continue;
+static void
+find_altered_opcode (struct mips_cl_insn *ip, const struct mips_opcode *end)
+{
+ const struct mips_opcode *mo;
- case 'Z':
- MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
- continue;
+ for (mo = ip->insn_mo; mo < end; mo++)
+ if (mo->pinfo != INSN_MACRO
+ && (ip->insn_opcode & mo->mask) == mo->match)
+ {
+ ip->insn_mo = mo;
+ return;
+ }
+ abort ();
+}
- case '0':
- case 'S':
- case 'P':
- case 'R':
- continue;
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
- case 'X':
- MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
- continue;
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+ find_altered_opcode (ip, &mips16_opcodes[bfd_mips16_num_opcodes]);
+}
- case 'Y':
- {
- int regno;
+/* IP is a microMIPS instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
- regno = va_arg (*args, int);
- regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
- MIPS16_INSERT_OPERAND (REG32R, insn, regno);
- }
- continue;
+static void
+find_altered_micromips_opcode (struct mips_cl_insn *ip)
+{
+ find_altered_opcode (ip, µmips_opcodes[bfd_micromips_num_opcodes]);
+}
- 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':
- {
- gas_assert (ep != NULL);
+/* For microMIPS macros, we need to generate a local number label
+ as the target of branches. */
+#define MICROMIPS_LABEL_CHAR '\037'
+static unsigned long micromips_target_label;
+static char micromips_target_name[32];
- if (ep->X_op != O_constant)
- *r = (int) BFD_RELOC_UNUSED + c;
- else
- {
- mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE,
- FALSE, &insn.insn_opcode, &insn.use_extend,
- &insn.extend);
- ep = NULL;
- *r = BFD_RELOC_UNUSED;
- }
- }
- continue;
+static char *
+micromips_label_name (void)
+{
+ char *p = micromips_target_name;
+ char symbol_name_temporary[24];
+ unsigned long l;
+ int i;
- case '6':
- MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
- continue;
- }
+ if (*p)
+ return p;
- break;
+ i = 0;
+ l = micromips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+ *p++ = LOCAL_LABEL_PREFIX;
+#endif
+ *p++ = 'L';
+ *p++ = MICROMIPS_LABEL_CHAR;
+ do
+ {
+ symbol_name_temporary[i++] = l % 10 + '0';
+ l /= 10;
}
+ while (l != 0);
+ while (i > 0)
+ *p++ = symbol_name_temporary[--i];
+ *p = '\0';
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
- append_insn (&insn, ep, r, TRUE);
+ return micromips_target_name;
}
-/*
- * Sign-extend 32-bit mode constants that have bit 31 set and all
- * higher bits unset.
- */
static void
-normalize_constant_expr (expressionS *ex)
+micromips_label_expr (expressionS *label_expr)
{
- 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);
+ label_expr->X_op = O_symbol;
+ label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+ label_expr->X_add_number = 0;
}
-/*
- * Sign-extend 32-bit mode address offsets that have bit 31 set and
- * all higher bits unset.
- */
static void
-normalize_address_expr (expressionS *ex)
+micromips_label_inc (void)
{
- 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);
+ micromips_target_label++;
+ *micromips_target_name = '\0';
}
-/*
- * 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)
+micromips_add_label (void)
{
- 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;
+ symbolS *s;
- if (MIPS_JALR_HINT_P (ep))
- {
- frag_grow (8);
- f = frag_more (0);
- }
- if (mips_opts.micromips)
- {
- jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs";
- if (MIPS_JALR_HINT_P (ep))
- macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
- else
- macro_build (NULL, jalr, "mj", PIC_CALL_REG);
- }
- else
- macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
- if (MIPS_JALR_HINT_P (ep))
- fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+ s = colon (micromips_label_name ());
+ micromips_label_inc ();
+ S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
}
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
+/* If assembling microMIPS code, then return the microMIPS reloc
+ corresponding to the requested one if any. Otherwise return
+ the reloc unchanged. */
+
+static bfd_reloc_code_real_type
+micromips_map_reloc (bfd_reloc_code_real_type reloc)
{
- gas_assert (! mips_opts.mips16);
+ static const bfd_reloc_code_real_type relocs[][2] =
+ {
+ /* Keep sorted incrementally by the left-hand key. */
+ { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
+ { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
+ { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
+ { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
+ { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
+ { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
+ { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
+ { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
+ { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
+ { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
+ { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
+ { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
+ { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
+ { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
+ { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
+ { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
+ { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
+ };
+ bfd_reloc_code_real_type r;
+ size_t i;
- if (ep->X_op != O_constant)
+ if (!mips_opts.micromips)
+ return reloc;
+ for (i = 0; i < ARRAY_SIZE (relocs); i++)
{
- 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));
+ r = relocs[i][0];
+ if (r > reloc)
+ return reloc;
+ if (r == reloc)
+ return relocs[i][1];
}
-
- macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+ return reloc;
}
-/* Generate a sequence of instructions to do a load or store from a constant
- offset off of a base register (breg) into/from a target register (treg),
- using AT if necessary. */
-static void
-macro_build_ldst_constoffset (expressionS *ep, const char *op,
- int treg, int breg, int dbl)
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+ Return true on success, storing the resolved value in RESULT. */
+
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+ offsetT *result)
{
- gas_assert (ep->X_op == O_constant);
+ switch (reloc)
+ {
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+ return TRUE;
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+ return TRUE;
- /* Right now, this routine can only handle signed 32-bit constants. */
- if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
- as_warn (_("operand overflow"));
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MIPS16_HI16_S:
+ *result = ((operand + 0x8000) >> 16) & 0xffff;
+ return TRUE;
- 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);
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MIPS16_HI16:
+ *result = (operand >> 16) & 0xffff;
+ return TRUE;
- if (!mips_opts.at)
- as_bad (_("Macro used $at after \".set noat\""));
- }
-}
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_LO16_PCREL:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MIPS16_LO16:
+ *result = operand & 0xffff;
+ return TRUE;
-/* set_at()
- * Generates code to set the $at register to true (one)
- * if reg is less than the immediate expression.
- */
-static void
-set_at (int reg, int unsignedp)
-{
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
- macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
- AT, reg, BFD_RELOC_LO16);
- else
- {
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ case BFD_RELOC_UNUSED:
+ *result = operand;
+ return TRUE;
+
+ default:
+ return FALSE;
}
}
-/* Warn if an expression is not a constant. */
+/* Output an instruction. IP is the instruction information.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. EXPANSIONP is true if the instruction is part of
+ a macro expansion. */
static void
-check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
{
- if (ex->X_op == O_big)
- as_bad (_("unsupported large constant"));
- else if (ex->X_op != O_constant)
- as_bad (_("Instruction %s requires absolute expression"),
- ip->insn_mo->name);
+ unsigned long prev_pinfo2, pinfo;
+ bfd_boolean relaxed_branch = FALSE;
+ enum append_method method;
+ bfd_boolean relax32;
+ int branch_disp;
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (ex);
-}
+ if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+ fix_loongson2f (ip);
-/* 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)
+ file_ase_mips16 |= mips_opts.mips16;
+ file_ase_micromips |= mips_opts.micromips;
-/* load_register()
- * This routine generates the least number of instructions necessary to load
- * an absolute expression value into a register.
- */
-static void
-load_register (int reg, expressionS *ep, int dbl)
-{
- int freg;
- expressionS hi32, lo32;
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ pinfo = ip->insn_mo->pinfo;
- if (ep->X_op != O_big)
+ /* Don't raise alarm about `nods' frags as they'll fill in the right
+ kind of nop in relaxation if required. */
+ if (mips_opts.micromips
+ && !expansionp
+ && !(history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent
+ && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+ && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
+ && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 2)
+ || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 4)))
+ as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
+ (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+
+ if (address_expr == NULL)
+ ip->complete_p = 1;
+ else if (reloc_type[0] <= BFD_RELOC_UNUSED
+ && reloc_type[1] == BFD_RELOC_UNUSED
+ && reloc_type[2] == BFD_RELOC_UNUSED
+ && address_expr->X_op == O_constant)
{
- gas_assert (ep->X_op == O_constant);
+ switch (*reloc_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ {
+ int shift;
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+ /* Shift is 2, unusually, for microMIPS JALX. */
+ shift = (mips_opts.micromips
+ && strcmp (ip->insn_mo->name, "jalx") != 0) ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x3ffffff);
+ ip->complete_p = 1;
+ }
+ break;
- 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;
- }
- }
+ case BFD_RELOC_MIPS16_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |=
+ (((address_expr->X_add_number & 0x7c0000) << 3)
+ | ((address_expr->X_add_number & 0xf800000) >> 7)
+ | ((address_expr->X_add_number & 0x3fffc) >> 2));
+ ip->complete_p = 1;
+ break;
- /* The value is larger than 32 bits. */
+ case BFD_RELOC_16_PCREL_S2:
+ {
+ int shift;
- if (!dbl || HAVE_32BIT_GPRS)
- {
- char value[32];
+ shift = mips_opts.micromips ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if (!mips_relax_branch)
+ {
+ if ((address_expr->X_add_number + (1 << (shift + 15)))
+ & ~((1 << (shift + 16)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0xffff);
+ }
+ }
+ break;
- 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;
- }
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ {
+ int shift;
- 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);
- }
+ shift = 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + (1 << (shift + 20)))
+ & ~((1 << (shift + 21)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x1fffff);
+ }
+ break;
- if (hi32.X_add_number == 0)
- freg = 0;
- else
- {
- int shift, bit;
- unsigned long hi, lo;
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ {
+ int shift;
- 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;
- }
+ shift = 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + (1 << (shift + 25)))
+ & ~((1 << (shift + 26)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x3ffffff);
+ }
+ break;
+
+ default:
+ {
+ offsetT value;
+
+ if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+ &value))
+ {
+ ip->insn_opcode |= value & 0xffff;
+ ip->complete_p = 1;
+ }
+ }
+ break;
}
+ }
- /* Check for 16bit shifted constant. We know that hi32 is
- non-zero, so start the mask on the first bit of the hi32
- value. */
- shift = 17;
- do
+ if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+ {
+ /* There are a lot of optimizations we could do that we don't.
+ In particular, we do not, in general, reorder instructions.
+ If you use gcc with optimization, it will reorder
+ instructions and generally do much more optimization then we
+ do here; repeating all that work in the assembler would only
+ benefit hand written assembly code, and does not seem worth
+ it. */
+ int nops = (mips_optimize == 0
+ ? nops_for_insn (0, history, NULL)
+ : nops_for_insn_or_target (0, history, ip));
+ if (nops > 0)
{
- unsigned long himask, lomask;
+ fragS *old_frag;
+ unsigned long old_frag_offset;
+ int i;
- 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;
+ old_frag = frag_now;
+ old_frag_offset = frag_now_fix ();
- 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));
+ for (i = 0; i < nops; i++)
+ add_fixed_insn (NOP_INSN);
+ insert_into_history (0, nops, NOP_INSN);
- /* 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)
+ if (listing)
{
- hi >>= 1;
- ++bit;
+ listing_prev_line ();
+ /* We may be at the start of a variant frag. In case we
+ are, make sure there is enough space for the frag
+ after the frags created by listing_prev_line. The
+ argument to frag_grow here must be at least as large
+ as the argument to all other calls to frag_grow in
+ this file. We don't have to worry about being in the
+ middle of a variant frag, because the variants insert
+ all needed nop instructions themselves. */
+ frag_grow (40);
}
- lo = hi;
- hi = 0;
- }
- /* 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)
- {
- expressionS tmp;
+ mips_move_text_labels ();
- /* 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;
- }
- }
-
- /* 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;
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING)
+ ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
}
}
- else
+ else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
{
- expressionS mid16;
+ int nops;
- if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
+ /* Work out how many nops in prev_nop_frag are needed by IP,
+ ignoring hazards generated by the first prev_nop_frag_since
+ instructions. */
+ nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
+ gas_assert (nops <= prev_nop_frag_holds);
+
+ /* Enforce NOPS as a minimum. */
+ if (nops > prev_nop_frag_required)
+ prev_nop_frag_required = nops;
+
+ if (prev_nop_frag_holds == prev_nop_frag_required)
{
- macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
- macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
- return;
+ /* Settle for the current number of nops. Update the history
+ accordingly (for the benefit of any future .set reorder code). */
+ prev_nop_frag = NULL;
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_holds, NOP_INSN);
}
-
- if (freg != 0)
+ else
{
- macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
- freg = reg;
+ /* Allow this instruction to replace one of the nops that was
+ tentatively added to prev_nop_frag. */
+ prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
+ prev_nop_frag_holds--;
+ prev_nop_frag_since++;
}
- mid16 = lo32;
- mid16.X_add_number >>= 16;
- macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- freg = reg;
}
- if ((lo32.X_add_number & 0xffff) != 0)
- macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
-}
-static inline void
-load_delay_nop (void)
-{
- if (!gpr_interlocks)
- macro_build (NULL, "nop", "");
-}
+ method = get_append_method (ip, address_expr, reloc_type);
+ branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
-/* Load an address into a register. */
+ dwarf2_emit_insn (0);
+ /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+ so "move" the instruction address accordingly.
-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;
- }
+ Also, it doesn't seem appropriate for the assembler to reorder .loc
+ entries. If this instruction is a branch that we are going to swap
+ with the previous instruction, the two instructions should be
+ treated as a unit, and the debug information for both instructions
+ should refer to the start of the branch sequence. Using the
+ current position is certainly wrong when swapping a 32-bit branch
+ and a 16-bit delay slot, since the current position would then be
+ in the middle of a branch. */
+ dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
- if (ep->X_op == O_constant)
+ relax32 = (mips_relax_branch
+ /* Don't try branch relaxation within .set nomacro, or within
+ .set noat if we use $at for PIC computations. If it turns
+ out that the branch was out-of-range, we'll get an error. */
+ && !mips_opts.warn_about_macros
+ && (mips_opts.at || mips_pic == NO_PIC)
+ /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
+ as they have no complementing branches. */
+ && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+
+ if (!HAVE_CODE_COMPRESSION
+ && address_expr
+ && relax32
+ && *reloc_type == BFD_RELOC_16_PCREL_S2
+ && delayed_branch_p (ip))
{
- load_register (reg, ep, HAVE_64BIT_ADDRESSES);
- return;
+ relaxed_branch = TRUE;
+ add_relaxed_insn (ip, (relaxed_branch_length
+ (NULL, NULL,
+ uncond_branch_p (ip) ? -1
+ : branch_likely_p (ip) ? 1
+ : 0)), 4,
+ RELAX_BRANCH_ENCODE
+ (AT,
+ uncond_branch_p (ip),
+ branch_likely_p (ip),
+ pinfo & INSN_WRITE_GPR_31,
+ 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
}
-
- if (mips_pic == NO_PIC)
+ else if (mips_opts.micromips
+ && address_expr
+ && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
+ || *reloc_type > BFD_RELOC_UNUSED)
+ && (delayed_branch_p (ip) || compact_branch_p (ip))
+ /* Don't try branch relaxation when users specify
+ 16-bit/32-bit instructions. */
+ && !forced_insn_length)
{
- /* 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.
+ bfd_boolean relax16 = (method != APPEND_ADD_COMPACT
+ && *reloc_type > BFD_RELOC_UNUSED);
+ int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
+ int uncond = uncond_branch_p (ip) ? -1 : 0;
+ int compact = compact_branch_p (ip) || method == APPEND_ADD_COMPACT;
+ int nods = method == APPEND_ADD_WITH_NOP;
+ int al = pinfo & INSN_WRITE_GPR_31;
+ int length32 = nods ? 8 : 4;
- 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
+ gas_assert (address_expr != NULL);
+ gas_assert (!mips_relax.sequence);
- 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)
+ relaxed_branch = TRUE;
+ if (nods)
+ method = APPEND_ADD;
+ if (relax32)
+ length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
+ add_relaxed_insn (ip, length32, relax16 ? 2 : 4,
+ RELAX_MICROMIPS_ENCODE (type, AT, mips_opts.insn32,
+ uncond, compact, al, nods,
+ relax32, 0, 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
+ {
+ bfd_boolean require_unextended;
+ bfd_boolean require_extended;
+ symbolS *symbol;
+ offsetT offset;
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
+ if (forced_insn_length != 0)
{
- if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (ep->X_add_symbol, 1))
- {
- relax_start (ep->X_add_symbol);
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
- mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
-
- if (*used_at == 0 && mips_opts.at)
- {
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
- macro_build (ep, "daddiu", "t,r,j", reg, reg,
- BFD_RELOC_MIPS_HIGHER);
- macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
- macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
- macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
- *used_at = 1;
- }
- else
- {
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (ep, "daddiu", "t,r,j", reg, reg,
- BFD_RELOC_MIPS_HIGHER);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
- }
-
- if (mips_relax.sequence)
- relax_end ();
+ require_unextended = forced_insn_length == 2;
+ require_extended = forced_insn_length == 4;
}
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 ();
+ require_unextended = (mips_opts.noautoextend
+ && !mips_opcode_32bit_p (ip->insn_mo));
+ require_extended = 0;
}
- }
- else if (!mips_big_got)
- {
- expressionS ex;
-
- /* If this is a reference to an external symbol, we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- Otherwise we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
- If there is a constant, it must be added in after.
- 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)
+ /* We need to set up a variant frag. */
+ gas_assert (address_expr != NULL);
+ /* Pass any `O_symbol' expression unchanged as an `expr_section'
+ symbol created by `make_expr_symbol' may not get a necessary
+ external relocation produced. */
+ if (address_expr->X_op == O_symbol)
{
- 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 ();
+ symbol = address_expr->X_add_symbol;
+ offset = address_expr->X_add_number;
}
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 (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);
- }
+ symbol = make_expr_symbol (address_expr);
+ offset = 0;
}
+ add_relaxed_insn (ip, 4, 0,
+ RELAX_MIPS16_ENCODE
+ (*reloc_type - BFD_RELOC_UNUSED,
+ require_unextended, require_extended,
+ delayed_branch_p (&history[0]),
+ history[0].mips16_absolute_jump_p),
+ symbol, offset);
}
- else if (mips_big_got)
+ else if (mips_opts.mips16 && insn_length (ip) == 2)
{
- 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)
-
- Otherwise, for a reference to a local symbol in old ABI, we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
- If there is a constant, it must be added in after.
+ if (!delayed_branch_p (ip))
+ /* Make sure there is enough room to swap this instruction with
+ a following jump instruction. */
+ frag_grow (6);
+ add_fixed_insn (ip);
+ }
+ else
+ {
+ if (mips_opts.mips16
+ && mips_opts.noreorder
+ && delayed_branch_p (&history[0]))
+ as_warn (_("extended instruction in delay slot"));
- 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)
+ if (mips_relax.sequence)
{
- 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);
- }
+ /* If we've reached the end of this frag, turn it into a variant
+ frag and record the information for the instructions we've
+ written so far. */
+ if (frag_room () < 4)
+ relax_close_frag ();
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
+ }
- 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_relax.sequence != 2)
+ {
+ if (mips_macro_warning.first_insn_sizes[0] == 0)
+ mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
+ mips_macro_warning.sizes[0] += insn_length (ip);
+ mips_macro_warning.insns[0]++;
}
- else
+ if (mips_relax.sequence != 1)
{
- 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 (mips_macro_warning.first_insn_sizes[1] == 0)
+ mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
+ mips_macro_warning.sizes[1] += insn_length (ip);
+ mips_macro_warning.insns[1]++;
+ }
- if (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);
- }
+ if (mips_opts.mips16)
+ {
+ ip->fixed_p = 1;
+ ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
}
+ add_fixed_insn (ip);
}
- else
- abort ();
- if (!mips_opts.at && *used_at == 1)
- as_bad (_("Macro used $at after \".set noat\""));
-}
+ if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
+ {
+ bfd_reloc_code_real_type final_type[3];
+ reloc_howto_type *howto0;
+ reloc_howto_type *howto;
+ int i;
-/* Move the contents of register SOURCE into register DEST. */
+ /* Perform any necessary conversion to microMIPS relocations
+ and find out how many relocations there actually are. */
+ for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
+ final_type[i] = micromips_map_reloc (reloc_type[i]);
-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
- && !(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);
-}
+ /* In a compound relocation, it is the final (outermost)
+ operator that determines the relocated field. */
+ howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+ if (!howto)
+ abort ();
-/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
- LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
- The two alternatives are:
+ if (i > 1)
+ howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
+ ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr,
+ howto0 && howto0->pc_relative,
+ final_type[0]);
- Global symbol Local sybmol
- ------------- ------------
- lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
- ... ...
- addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+ /* Tag symbols that have a R_MIPS16_26 relocation against them. */
+ if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
+ *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
- 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. */
+ /* These relocations can have an addend that won't fit in
+ 4 octets for 64bit assembly. */
+ if (GPR_SIZE == 64
+ && ! howto->partial_inplace
+ && (reloc_type[0] == BFD_RELOC_16
+ || reloc_type[0] == BFD_RELOC_32
+ || reloc_type[0] == BFD_RELOC_MIPS_JMP
+ || reloc_type[0] == BFD_RELOC_GPREL16
+ || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+ || reloc_type[0] == BFD_RELOC_GPREL32
+ || reloc_type[0] == BFD_RELOC_64
+ || reloc_type[0] == BFD_RELOC_CTOR
+ || reloc_type[0] == BFD_RELOC_MIPS_SUB
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+ || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+ || reloc_type[0] == BFD_RELOC_MIPS_REL16
+ || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+ || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+ || hi16_reloc_p (reloc_type[0])
+ || lo16_reloc_p (reloc_type[0])))
+ ip->fixp[0]->fx_no_overflow = 1;
-static void
-load_got_offset (int dest, expressionS *local)
-{
- expressionS global;
+ /* These relocations can have an addend that won't fit in 2 octets. */
+ if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
+ ip->fixp[0]->fx_no_overflow = 1;
- global = *local;
- global.X_add_number = 0;
+ if (mips_relax.sequence)
+ {
+ if (mips_relax.first_fixup == 0)
+ mips_relax.first_fixup = ip->fixp[0];
+ }
+ else if (reloc_needs_lo_p (*reloc_type))
+ {
+ struct mips_hi_fixup *hi_fixup;
- 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 ();
-}
+ /* Reuse the last entry if it already has a matching %lo. */
+ hi_fixup = mips_hi_fixup_list;
+ if (hi_fixup == 0
+ || !fixup_has_matching_lo_p (hi_fixup->fixp))
+ {
+ hi_fixup = XNEW (struct mips_hi_fixup);
+ hi_fixup->next = mips_hi_fixup_list;
+ mips_hi_fixup_list = hi_fixup;
+ }
+ hi_fixup->fixp = ip->fixp[0];
+ hi_fixup->seg = now_seg;
+ }
-static void
-add_got_offset (int dest, expressionS *local)
-{
- expressionS global;
+ /* Add fixups for the second and third relocations, if given.
+ Note that the ABI allows the second relocation to be
+ against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
+ moment we only use RSS_UNDEF, but we could add support
+ for the others if it ever becomes necessary. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] != BFD_RELOC_UNUSED)
+ {
+ ip->fixp[i] = fix_new (ip->frag, ip->where,
+ ip->fixp[0]->fx_size, NULL, 0,
+ FALSE, final_type[i]);
- global.X_op = O_constant;
- global.X_op_symbol = NULL;
- global.X_add_symbol = NULL;
- global.X_add_number = local->X_add_number;
+ /* Use fx_tcbit to mark compound relocs. */
+ ip->fixp[0]->fx_tcbit = 1;
+ ip->fixp[i]->fx_tcbit = 1;
+ }
+ }
- 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 ();
-}
+ /* Update the register mask information. */
+ mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+ mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
-static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
-{
- expressionS global;
- int hold_mips_optimize;
+ switch (method)
+ {
+ case APPEND_ADD:
+ insert_into_history (0, 1, ip);
+ 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 APPEND_ADD_WITH_NOP:
+ {
+ struct mips_cl_insn *nop;
- 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 ();
+ insert_into_history (0, 1, ip);
+ nop = get_delay_slot_nop (ip);
+ add_fixed_insn (nop);
+ insert_into_history (0, 1, nop);
+ if (mips_relax.sequence)
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
+ }
+ break;
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
-}
+ case APPEND_ADD_COMPACT:
+ /* Convert MIPS16 jr/jalr into a "compact" jump. */
+ if (mips_opts.mips16)
+ {
+ ip->insn_opcode |= 0x0080;
+ find_altered_mips16_opcode (ip);
+ }
+ /* Convert microMIPS instructions. */
+ else if (mips_opts.micromips)
+ {
+ /* jr16->jrc */
+ if ((ip->insn_opcode & 0xffe0) == 0x4580)
+ ip->insn_opcode |= 0x0020;
+ /* b16->bc */
+ else if ((ip->insn_opcode & 0xfc00) == 0xcc00)
+ ip->insn_opcode = 0x40e00000;
+ /* beqz16->beqzc, bnez16->bnezc */
+ else if ((ip->insn_opcode & 0xdc00) == 0x8c00)
+ {
+ unsigned long regno;
-/* 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.
+ regno = ip->insn_opcode >> MICROMIPSOP_SH_MD;
+ regno &= MICROMIPSOP_MASK_MD;
+ regno = micromips_to_32_reg_d_map[regno];
+ ip->insn_opcode = (((ip->insn_opcode << 9) & 0x00400000)
+ | (regno << MICROMIPSOP_SH_RS)
+ | 0x40a00000) ^ 0x00400000;
+ }
+ /* beqz->beqzc, bnez->bnezc */
+ else if ((ip->insn_opcode & 0xdfe00000) == 0x94000000)
+ ip->insn_opcode = ((ip->insn_opcode & 0x001f0000)
+ | ((ip->insn_opcode >> 7) & 0x00400000)
+ | 0x40a00000) ^ 0x00400000;
+ /* beq $0->beqzc, bne $0->bnezc */
+ else if ((ip->insn_opcode & 0xdc1f0000) == 0x94000000)
+ ip->insn_opcode = (((ip->insn_opcode >>
+ (MICROMIPSOP_SH_RT - MICROMIPSOP_SH_RS))
+ & (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS))
+ | ((ip->insn_opcode >> 7) & 0x00400000)
+ | 0x40a00000) ^ 0x00400000;
+ else
+ abort ();
+ find_altered_micromips_opcode (ip);
+ }
+ else
+ abort ();
+ install_insn (ip);
+ insert_into_history (0, 1, ip);
+ break;
- Code like this is produced in the noreorder mode:
+ case APPEND_SWAP:
+ {
+ struct mips_cl_insn delay = history[0];
- BRNEG <args>, 1f
- nop
- b <sym>
- delay slot (executed only if branch taken)
- 1:
+ if (relaxed_branch || delay.frag != ip->frag)
+ {
+ /* Add the delay slot instruction to the end of the
+ current frag and shrink the fixed part of the
+ original frag. If the branch occupies the tail of
+ the latter, move it backwards to cover the gap. */
+ delay.frag->fr_fix -= branch_disp;
+ if (delay.frag == ip->frag)
+ move_insn (ip, ip->frag, ip->where - branch_disp);
+ add_fixed_insn (&delay);
+ }
+ else
+ {
+ /* If this is not a relaxed branch and we are in the
+ same frag, then just swap the instructions. */
+ move_insn (ip, delay.frag, delay.where);
+ move_insn (&delay, ip->frag, ip->where + insn_length (ip));
+ }
+ history[0] = *ip;
+ delay.fixed_p = 1;
+ insert_into_history (0, 1, &delay);
+ }
+ break;
+ }
- or, if CALL is set:
+ /* If we have just completed an unconditional branch, clear the history. */
+ if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
+ || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
+ {
+ unsigned int i;
- BRNEG <args>, 1f
- nop
- bal <sym>
- delay slot (executed only if branch taken)
- 1:
+ mips_no_prev_insn ();
- In the reorder mode the delay slot would be filled with a nop anyway,
- so code produced is simply:
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].cleared_p = 1;
+ }
- BR <args>, <sym>
- nop
+ /* We need to emit a label at the end of branch-likely macros. */
+ if (emit_branch_likely_macro)
+ {
+ emit_branch_likely_macro = FALSE;
+ micromips_add_label ();
+ }
- This function is used when producing code for the microMIPS ASE that
- does not implement branch likely instructions in hardware. */
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+}
+
+/* Forget that there was any previous instruction or label.
+ When BRANCH is true, the branch history is also flushed. */
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)
+mips_no_prev_insn (void)
{
- int noreorder = mips_opts.noreorder;
- expressionS expr1;
+ prev_nop_frag = NULL;
+ insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+ mips_clear_insn_labels ();
+}
- 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");
+/* This function must be called before we emit something other than
+ instructions. It is like mips_no_prev_insn except that it inserts
+ any NOPS that might be needed by previous instructions. */
- /* Set to true so that append_insn adds a label. */
- emit_branch_likely_macro = TRUE;
- }
- else
+void
+mips_emit_delays (void)
+{
+ if (! mips_opts.noreorder)
{
- macro_build (ep, br, fmt, sreg, treg);
- macro_build (NULL, "nop", "");
+ int nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
+ {
+ while (nops-- > 0)
+ add_fixed_insn (NOP_INSN);
+ mips_move_text_labels ();
+ }
}
- end_noreorder ();
+ mips_no_prev_insn ();
}
-/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
- the condition code tested. EP specifies the branch target. */
+/* Start a (possibly nested) noreorder block. */
static void
-macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+start_noreorder (void)
{
- const int call = 0;
- const char *brneg;
- const char *br;
-
- switch (type)
+ if (mips_opts.noreorder == 0)
{
- 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 ();
+ unsigned int i;
+ int nops;
+
+ /* None of the instructions before the .set noreorder can be moved. */
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].fixed_p = 1;
+
+ /* Insert any nops that might be needed between the .set noreorder
+ block and the previous instructions. We will later remove any
+ nops that turn out not to be needed. */
+ nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
+ {
+ if (mips_optimize != 0)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (nops * NOP_INSN_SIZE);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ add_fixed_insn (NOP_INSN);
+
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ mips_move_text_labels ();
+ }
+ mips_mark_labels ();
+ mips_clear_insn_labels ();
}
- macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
+ mips_opts.noreorder++;
+ mips_any_noreorder = 1;
}
-/* Emit a two-argument branch macro specified by TYPE, using SREG as
- the register tested. EP specifies the branch target. */
+/* End a nested noreorder block. */
static void
-macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+end_noreorder (void)
{
- const char *brneg = NULL;
- const char *br;
- int call = 0;
-
- switch (type)
+ mips_opts.noreorder--;
+ if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
{
- 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 = "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 = "bltzals";
- brneg = "bgez";
- call = 1;
- break;
- default:
- abort ();
+ /* Commit to inserting prev_nop_frag_required nops and go back to
+ handling nop insertion the .set reorder way. */
+ prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+ * NOP_INSN_SIZE);
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_required, NOP_INSN);
+ prev_nop_frag = NULL;
}
- 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. */
+/* Sign-extend 32-bit mode constants that have bit 31 set and all
+ higher bits unset. */
static void
-macro_build_branch_rsrt (int type, expressionS *ep,
- unsigned int sreg, unsigned int treg)
+normalize_constant_expr (expressionS *ex)
{
- const char *brneg = NULL;
- const int call = 0;
- const char *br;
-
- switch (type)
- {
- case M_BEQ:
- case M_BEQ_I:
- br = "beq";
- break;
- case M_BEQL:
- case M_BEQL_I:
- br = mips_opts.micromips ? "beq" : "beql";
- brneg = "bne";
- break;
- case M_BNE:
- case M_BNE_I:
- br = "bne";
- break;
- case M_BNEL:
- case M_BNEL_I:
- br = mips_opts.micromips ? "bne" : "bnel";
- brneg = "beq";
- break;
- default:
- abort ();
- }
- if (mips_opts.micromips && brneg)
- macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
- else
- macro_build (ep, br, "s,t,p", sreg, treg);
+ 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);
}
-/*
- * 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.
- */
+/* Sign-extend 32-bit mode address offsets that have bit 31 set and
+ all higher bits unset. */
+
static void
-macro (struct mips_cl_insn *ip)
+normalize_address_expr (expressionS *ex)
{
- 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 off12 = 0;
- int call = 0;
- int jals = 0;
- int dbl = 0;
- int imm = 0;
- int ust = 0;
- int lp = 0;
- int ab = 0;
- int off;
- offsetT maxnum;
- bfd_reloc_code_real_type r;
- int hold_mips_optimize;
+ 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);
+}
- gas_assert (! mips_opts.mips16);
+/* Try to match TOKENS against OPCODE, storing the result in INSN.
+ Return true if the match was successful.
- 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;
+ 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. */
- label_expr.X_op = O_constant;
- label_expr.X_op_symbol = NULL;
- label_expr.X_add_symbol = NULL;
- label_expr.X_add_number = 0;
+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;
- expr1.X_op = O_constant;
- expr1.X_op_symbol = NULL;
- expr1.X_add_symbol = NULL;
- expr1.X_add_number = 1;
+ 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;
- switch (mask)
+ 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)
{
- case M_DABS:
- dbl = 1;
- case M_ABS:
- /* bgez $a0,1f
- move v0,$a0
- sub v0,$zero,$a0
- 1:
- */
+ 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;
+ }
+ }
- start_noreorder ();
+ /* Treat elided base registers as $0. */
+ if (strcmp (args, "(b)") == 0)
+ args += 3;
- 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 ();
+ if (args[0] == '+')
+ switch (args[1])
+ {
+ case 'K':
+ case 'N':
+ /* The register suffix is optional. */
+ args += 2;
+ break;
+ }
- end_noreorder ();
- break;
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
- 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)
+ /* 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;
+ }
+
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
+
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
{
- macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
- break;
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
}
- 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)
+ if (*args == '#')
{
- macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
- break;
+ ++args;
+ if (arg.token->type == OT_DOUBLE_CHAR
+ && arg.token->u.ch == *args)
+ {
+ ++arg.token;
+ continue;
+ }
+ return FALSE;
}
- 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)
+ /* Handle special macro operands. Work out the properties of
+ other operands. */
+ arg.opnum += 1;
+ switch (*args)
{
- if (mask != M_NOR_I)
- macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
- else
+ case '-':
+ switch (args[1])
{
- macro_build (&imm_expr, "ori", "t,r,i",
- treg, sreg, BFD_RELOC_LO16);
- macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
+ case 'A':
+ *offset_reloc = BFD_RELOC_MIPS_19_PCREL_S2;
+ break;
+
+ case 'B':
+ *offset_reloc = BFD_RELOC_MIPS_18_PCREL_S3;
+ break;
}
break;
- }
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
- break;
+ case '+':
+ switch (args[1])
+ {
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
- case M_BALIGN:
- switch (imm_expr.X_add_number)
- {
- case 0:
- macro_build (NULL, "nop", "");
- break;
- case 2:
- macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
+ case '\'':
+ *offset_reloc = BFD_RELOC_MIPS_26_PCREL_S2;
+ break;
+
+ case '\"':
+ *offset_reloc = BFD_RELOC_MIPS_21_PCREL_S2;
+ break;
+ }
break;
- default:
- macro_build (NULL, "balign", "t,s,2", treg, sreg,
- (int) imm_expr.X_add_number);
+
+ 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':
+ 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;
+
+ case 'F':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, TRUE))
+ return FALSE;
+ continue;
+
+ case 'L':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, FALSE))
+ return FALSE;
+ continue;
+
+ case 'f':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, TRUE))
+ return FALSE;
+ continue;
+
+ case 'l':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, FALSE))
+ return FALSE;
+ continue;
+
+ case 'p':
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
break;
- }
- break;
- case M_BC1FL:
- case M_BC1TL:
- case M_BC2FL:
- case M_BC2TL:
- gas_assert (mips_opts.micromips);
- macro_build_branch_ccl (mask, &offset_expr,
- EXTRACT_OPERAND (1, BCC, *ip));
- break;
+ case 'a':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
- case M_BEQ_I:
- case M_BEQL_I:
- case M_BNE_I:
- case M_BNEL_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- treg = 0;
- else
- {
- treg = AT;
- used_at = 1;
- load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+ case 'm':
+ gas_assert (mips_opts.micromips);
+ c = args[1];
+ switch (c)
+ {
+ case 'D':
+ case 'E':
+ if (!forced_insn_length)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ else if (c == 'D')
+ *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+ else
+ *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
+ break;
+ }
+ break;
}
- /* 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
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args)
+ : decode_mips_operand (args));
+ if (!operand)
+ abort ();
+
+ /* Skip prefixes. */
+ if (*args == '+' || *args == 'm' || *args == '-')
+ args++;
+
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
{
- 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);
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
}
- break;
- case M_BGEZL:
- case M_BGEZALL:
- case M_BGTZL:
- case M_BLEZL:
- case M_BLTZL:
- case M_BLTZALL:
- macro_build_branch_rs (mask, &offset_expr, sreg);
- break;
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
+}
- case M_BGTL_I:
- likely = 1;
- case M_BGT_I:
- /* Check for > max integer. */
- maxnum = 0x7fffffff;
- if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+/* Like match_insn, but for MIPS16. */
+
+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;
+ int required_insn_length;
+ struct mips_arg_info arg;
+ int relax_char;
+
+ if (forced_insn_length)
+ required_insn_length = forced_insn_length;
+ else if (mips_opts.noautoextend && !mips_opcode_32bit_p (opcode))
+ required_insn_length = 2;
+ else
+ required_insn_length = 0;
+
+ 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;
+
+ 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 (arg.token->type == OT_END)
{
- maxnum <<= 16;
- maxnum |= 0xffff;
- maxnum <<= 16;
- maxnum |= 0xffff;
+ offsetT value;
+
+ /* 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;
+ }
+ }
+
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
+
+ /* 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,
+ required_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 (required_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;
+
+ check_completed_insn (&arg);
+ return TRUE;
}
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= maxnum
- && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
+
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
{
- do_false:
- /* Result is always false. */
- if (! likely)
- macro_build (NULL, "nop", "");
- else
- macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
- break;
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
}
- 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)
+
+ arg.opnum += 1;
+ c = *args;
+ switch (c)
{
- macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
- &offset_expr, sreg);
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ relax_char = c;
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);
+
+ 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;
break;
}
- maxnum = 0x7fffffff;
- if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+
+ operand = decode_mips16_operand (c, mips_opcode_32bit_p (opcode));
+ if (!operand)
+ abort ();
+
+ if (operand->type != OP_PCREL)
{
- maxnum <<= 16;
- maxnum |= 0xffff;
- maxnum <<= 16;
- maxnum |= 0xffff;
+ 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;
+ }
+
+ /* 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;
+
+ /* '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;
+
+ relax_char = c;
+ continue;
+ }
}
- maxnum = - maxnum - 1;
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number <= maxnum
- && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
{
- do_true:
- /* result is always true */
- as_warn (_("Branch %s is always true"), ip->insn_mo->name);
- macro_build (&offset_expr, "b", "p");
- break;
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
}
- used_at = 1;
- set_at (sreg, 0);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- break;
- 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
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
+}
+
+/* Record that the current instruction is invalid for the current ISA. */
+
+static void
+match_invalid_for_isa (void)
+{
+ 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);
+}
+
+/* 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. */
+
+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)
+{
+ 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))
{
- 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);
+ seen_valid_for_isa = TRUE;
+ if (is_size_valid (opcode))
+ {
+ bfd_boolean delay_slot_ok;
+
+ 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;
+ }
+ }
}
- break;
+ ++opcode;
+ }
+ while (opcode < past && strcmp (opcode->name, first->name) == 0);
- 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);
+ /* 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)
+ {
+ if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
+ lax_match, TRUE))
+ return TRUE;
+ abort ();
+ }
+
+ /* 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;
+ }
+
+ /* 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
+ set_insn_error_i
+ (0, _("unrecognized %d-bit version of microMIPS opcode"),
+ 8 * forced_insn_length);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* 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)
+{
+ const struct mips_opcode *opcode;
+ bfd_boolean seen_valid_for_isa;
+ bfd_boolean seen_valid_for_size;
+
+ /* 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;
+ seen_valid_for_size = FALSE;
+ opcode = first;
+ do
+ {
+ gas_assert (strcmp (opcode->name, first->name) == 0);
+ if (is_opcode_valid_16 (opcode))
{
- used_at = 1;
- set_at (sreg, 1);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
+ seen_valid_for_isa = TRUE;
+ if (is_size_valid_16 (opcode))
+ {
+ seen_valid_for_size = TRUE;
+ if (match_mips16_insn (insn, opcode, tokens))
+ return TRUE;
+ }
}
- break;
+ ++opcode;
+ }
+ while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
+ && strcmp (opcode->name, first->name) == 0);
- 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);
+ /* 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;
+ }
+
+ /* 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 (forced_insn_length == 2)
+ set_insn_error
+ (0, _("unrecognized unextended version of MIPS16 opcode"));
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;
+ set_insn_error
+ (0, _("unrecognized extended version of MIPS16 opcode"));
+ return TRUE;
+ }
- case M_BGTUL:
- likely = 1;
- case M_BGTU:
- if (treg == 0)
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, sreg, ZERO);
- else if (sreg == 0)
- goto do_false;
- else
- {
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
-
- case M_BLEL:
- likely = 1;
- case M_BLE:
- if (treg == 0)
- macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
- else if (sreg == 0)
- macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
- else
- {
- used_at = 1;
- macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+ return FALSE;
+}
- case M_BLEL_I:
- likely = 1;
- case M_BLE_I:
- maxnum = 0x7fffffff;
- if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
- {
- maxnum <<= 16;
- maxnum |= 0xffff;
- maxnum <<= 16;
- maxnum |= 0xffff;
- }
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= maxnum
- && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
- 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;
+/* Set up global variables for the start of a new macro. */
- 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;
+static void
+macro_start (void)
+{
+ memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+ memset (&mips_macro_warning.first_insn_sizes, 0,
+ sizeof (mips_macro_warning.first_insn_sizes));
+ memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
+ mips_macro_warning.delay_slot_p = (mips_opts.noreorder
+ && delayed_branch_p (&history[0]));
+ if (history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent
+ && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+ && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
+ mips_macro_warning.delay_slot_length = 0;
+ else
+ 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;
+}
- case M_BLEUL_I:
- likely = 1;
- case M_BLEU_I:
- if (sreg == 0
- || (HAVE_32BIT_GPRS
- && imm_expr.X_op == O_constant
- && imm_expr.X_add_number == -1))
- goto do_true;
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
- /* FALLTHROUGH */
- case M_BLTU_I:
- case M_BLTUL_I:
- if (mask == M_BLTUL_I)
- likely = 1;
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- goto do_false;
- else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, sreg, ZERO);
- else
- {
- used_at = 1;
- set_at (sreg, 1);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+/* 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. */
- 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);
- }
- break;
+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;
+}
- case M_BLTUL:
- likely = 1;
- case M_BLTU:
- if (treg == 0)
- goto do_false;
- else if (sreg == 0)
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, ZERO, treg);
- else
- {
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+/* Finish up a macro. Emit warnings as appropriate. */
- case M_DEXT:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
+static void
+macro_end (void)
+{
+ /* Relaxation warning flags. */
+ relax_substateT subtype = 0;
- 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;
- }
+ /* 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;
+ }
- 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;
- }
+ /* Check instruction count requirements. */
+ if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
+ {
+ if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
+ subtype |= RELAX_SECOND_LONGER;
+ if (mips_opts.warn_about_macros)
+ subtype |= RELAX_NOMACRO;
+ if (mips_macro_warning.delay_slot_p)
+ subtype |= RELAX_DELAY_SLOT;
+ }
- if (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;
+ /* 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;
- case M_DINS:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
+ 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;
+ }
- if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
- {
- as_bad (_("Unsupported large constant"));
- pos = size = 1;
- }
- else
- {
- pos = imm_expr.X_add_number;
- size = imm2_expr.X_add_number;
- }
+ /* If 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 (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;
- }
+ s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn ("%s", msg);
+ subtype &= ~s;
+ }
- if (pos < 32 && (pos + size - 1) < 32)
- {
- s = "dins";
- fmt = "t,r,+A,+B";
- }
- else if (pos >= 32)
- {
- s = "dinsu";
- fmt = "t,r,+E,+F";
- }
- else
- {
- s = "dinsm";
- fmt = "t,r,+A,+F";
- }
- macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
- (int) (pos + size - 1));
- }
- break;
+ /* If 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;
+}
- 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;
- }
+/* Instruction operand formats used in macros that vary between
+ standard MIPS and microMIPS code. */
- 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", "");
+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,|" };
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
+#define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
+#define COP12_FMT (ISA_IS_R6 (mips_opts.isa) ? "E,+:(d)" \
+ : 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 LL_SC_FMT (ISA_IS_R6 (mips_opts.isa) ? "t,+j(b)" \
+ : 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])
- macro_build (NULL, "break", BRK_FMT, 6);
- }
- if (mips_opts.micromips)
- micromips_add_label ();
- macro_build (NULL, s, MFHL_FMT, dreg);
- 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 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;
- }
+static void
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+{
+ int i, next;
- 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;
+ 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 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);
+/* 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. */
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
- macro_build (NULL, "break", BRK_FMT, 7);
- if (mips_opts.micromips)
- micromips_add_label ();
- }
- macro_build (NULL, s2, MFHL_FMT, dreg);
- break;
+static void
+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 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. */
+ va_start (args, fmt);
- if (dbl && HAVE_32BIT_GPRS)
- as_warn (_("dla used to load 32-bit register"));
+ if (mips_opts.mips16)
+ {
+ mips16_macro_build (ep, name, fmt, &args);
+ va_end (args);
+ return;
+ }
- if (!dbl && HAVE_64BIT_OBJECTS)
- as_warn (_("la used to load 64-bit address"));
+ 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 (offset_expr.X_op == O_constant
- && offset_expr.X_add_number >= -0x8000
- && offset_expr.X_add_number < 0x8000)
+ 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))
{
- macro_build (&offset_expr, ADDRESS_ADDI_INSN,
- "t,r,j", treg, sreg, BFD_RELOC_LO16);
- break;
+ if (is_delay_slot_valid (amo))
+ {
+ mo = amo;
+ break;
+ }
+ else if (!mo)
+ mo = amo;
}
- if (mips_opts.at && (treg == breg))
- {
- tempreg = AT;
- used_at = 1;
- }
- else
- {
- tempreg = treg;
- }
+ ++amo;
+ gas_assert (amo->name);
+ }
+ while (strcmp (name, amo->name) == 0);
- if (offset_expr.X_op != O_symbol
- && offset_expr.X_op != O_constant)
+ gas_assert (mo);
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
+ {
+ switch (*fmt)
{
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
- }
-
- if (offset_expr.X_op == O_constant)
- load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
- else if (mips_pic == NO_PIC)
- {
- /* If this is a reference to a GP relative symbol, we want
- addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
- Otherwise we want
- lui $tempreg,<sym> (BFD_RELOC_HI16_S)
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
- If we have a constant, we need two instructions anyhow,
- so we may as well always use the latter form.
-
- With 64bit address space and a usable $at we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- lui $at,<sym> (BFD_RELOC_HI16_S)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- daddiu $at,<sym> (BFD_RELOC_LO16)
- dsll32 $tempreg,0
- daddu $tempreg,$tempreg,$at
-
- If $at is already in use, we use a path which is suboptimal
- on superscalar processors.
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_LO16)
-
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
- {
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
+ case ',':
+ case '(':
+ case ')':
+ case 'z':
+ break;
- if (used_at == 0 && mips_opts.at)
- {
- macro_build (&offset_expr, "lui", LUI_FMT,
- tempreg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (&offset_expr, "lui", LUI_FMT,
- AT, BFD_RELOC_HI16_S);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
- 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 '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 (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;
+ case 'o':
+ macro_read_relocs (&args, r);
+ 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 '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;
- 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 'p':
+ gas_assert (ep != NULL);
- 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.
+ /*
+ * 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 (offset_expr.X_add_number == 0)
+ if (ep->X_op == O_constant)
{
- if (mips_pic == SVR4_PIC
- && breg == 0
- && (call || tempreg == PIC_CALL_REG))
- lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+ /* For microMIPS we always use relocations for branches.
+ So we should not resolve immediate values. */
+ gas_assert (!mips_opts.micromips);
- 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);
+ 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
- {
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number =
- ((offset_expr.X_add_number + 0x8000) & 0xffff) - 0x8000;
- 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;
+ *r = BFD_RELOC_16_PCREL_S2;
+ 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 'a':
+ gas_assert (ep != NULL);
+ *r = BFD_RELOC_MIPS_JMP;
+ break;
- If we have a small constant, and this is a reference to
- an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
- addiu $tempreg,$tempreg,<constant>
+ default:
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (fmt)
+ : decode_mips_operand (fmt));
+ if (!operand)
+ abort ();
- 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
+ uval = va_arg (args, int);
+ if (operand->type == OP_CLO_CLZ_DEST)
+ uval |= (uval << 5);
+ insn_insert_operand (&insn, operand, uval);
- (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
- local symbols, even though it introduces an additional
- instruction. */
+ if (*fmt == '+' || *fmt == 'm' || *fmt == '-')
+ ++fmt;
+ break;
+ }
+ }
+ va_end (args);
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
- if (offset_expr.X_add_number)
- {
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
-
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ append_insn (&insn, ep, r, TRUE);
+}
- if (expr1.X_add_number >= -0x8000
- && expr1.X_add_number < 0x8000)
- {
- macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- }
- else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
- {
- /* If we are going to add in a base register, and the
- target register and the base register are the same,
- then we are using AT as a temporary register. Since
- we want to load the constant into AT, we add our
- current AT (from the global offset table) and the
- register into the register now, and pretend we were
- not using a base register. */
- if (breg != treg)
- dreg = tempreg;
- else
- {
- gas_assert (tempreg == AT);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- dreg = treg;
- add_breg_early = 1;
- }
+static void
+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};
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- dreg, dreg, AT);
+ mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+ gas_assert (mo);
+ gas_assert (strcmp (name, mo->name) == 0);
- used_at = 1;
- }
- else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+ while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
+ {
+ ++mo;
+ gas_assert (mo->name);
+ gas_assert (strcmp (name, mo->name) == 0);
+ }
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
+ {
+ int c;
- 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)
+ c = *fmt;
+ switch (c)
{
- 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;
+ case ',':
+ case '(':
+ case ')':
+ break;
- /* 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)
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
+ 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)
- 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)
+ 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 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
- */
+ gas_assert (ep != NULL);
- 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;
- }
+ 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;
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+ default:
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
- used_at = 1;
- }
- offset_expr.X_add_number =
- ((expr1.X_add_number + 0x8000) & 0xffff) - 0x8000;
- relax_switch ();
+ insn_insert_operand (&insn, operand, va_arg (*args, int));
+ break;
+ }
+ }
- if (gpdelay)
- {
- /* This is needed because this instruction uses $gp, but
- the first instruction on the main stream does not. */
- macro_build (NULL, "nop", "");
- }
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
- 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;
- }
+ append_insn (&insn, ep, r, TRUE);
+}
- 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>
-
- 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
-
- 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. */
-
- relax_start (offset_expr.X_add_symbol);
-
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
-
- 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)
- ;
- 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;
- }
-
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
-
- used_at = 1;
- }
- else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+/*
+ * 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;
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
- tempreg, BFD_RELOC_MIPS_GOT_OFST);
- if (add_breg_early)
- {
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, tempreg, breg);
- breg = 0;
- tempreg = treg;
- }
- relax_end ();
- }
+ 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
- abort ();
-
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
- break;
+ macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+ }
+ else
+ macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+ if (MIPS_JALR_HINT_P (ep))
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+}
- case M_MSGSND:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | (0x01);
- macro_build (NULL, "c2", "C", temp);
- }
- break;
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+ gas_assert (! mips_opts.mips16);
- case M_MSGLD:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (0x02);
- macro_build (NULL, "c2", "C", temp);
- }
- break;
+ if (ep->X_op != O_constant)
+ {
+ gas_assert (ep->X_op == O_symbol);
+ /* _gp_disp is a special case, used from s_cpload.
+ __gnu_local_gp is used if mips_no_shared. */
+ gas_assert (mips_pic == NO_PIC
+ || (! HAVE_NEWABI
+ && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
+ || (! mips_in_shared
+ && strcmp (S_GET_NAME (ep->X_add_symbol),
+ "__gnu_local_gp") == 0));
+ }
- case M_MSGLD_T:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | (0x02);
- macro_build (NULL, "c2", "C", temp);
- }
- break;
+ macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
- case M_MSGWAIT:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", 3);
- break;
+/* Generate a sequence of instructions to do a load or store from a constant
+ offset off of a base register (breg) into/from a target register (treg),
+ using AT if necessary. */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+ int treg, int breg, int dbl)
+{
+ gas_assert (ep->X_op == O_constant);
- case M_MSGWAIT_T:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | 0x03;
- macro_build (NULL, "c2", "C", temp);
- }
- break;
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
- 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;
+ /* 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"));
- /* 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);
- jals = 1;
- goto jal;
- case M_JAL_1:
- dreg = RA;
- /* Fall through. */
- case M_JAL_2:
- jal:
- if (mips_pic == NO_PIC)
- {
- s = jals ? "jalrs" : "jalr";
- if (mips_opts.micromips && dreg == RA)
- macro_build (NULL, s, "mj", sreg);
- else
- macro_build (NULL, s, JALR_FMT, dreg, sreg);
- }
- else
- {
- int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
- && mips_cprestore_offset >= 0);
+ if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+ {
+ /* Signed 16-bit offset will fit in the op. Easy! */
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ }
+ else
+ {
+ /* 32-bit offset, need multiple instructions and AT, like:
+ lui $tempreg,const_hi (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,const_lo($tempreg) (BFD_RELOC_LO16)
+ to handle the complete offset. */
+ macro_build_lui (ep, AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
- if (sreg != PIC_CALL_REG)
- as_warn (_("MIPS PIC call to register other than $25"));
+ if (!mips_opts.at)
+ as_bad (_("macro used $at after \".set noat\""));
+ }
+}
- s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore)
- ? "jalrs" : "jalr");
- if (mips_opts.micromips && dreg == RA)
- 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);
- }
- }
- }
+/* 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_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+ AT, reg, BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ }
+}
- break;
+/* 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)
- case M_JALS_A:
- gas_assert (mips_opts.micromips);
- 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)
+/* load_register()
+ * This routine generates the least number of instructions necessary to load
+ * an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+ int freg;
+ expressionS hi32, lo32;
- 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 ();
- }
+ if (ep->X_op != O_big)
+ {
+ gas_assert (ep->X_op == O_constant);
- 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;
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
- gpdelay = reg_needs_delay (mips_gp_register);
- macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
- BFD_RELOC_MIPS_CALL_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
- PIC_CALL_REG, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
- PIC_CALL_REG);
- load_delay_nop ();
- relax_switch ();
- if (gpdelay)
- macro_build (NULL, "nop", "");
- }
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
- mips_gp_register);
- load_delay_nop ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
- relax_end ();
- macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
+ if (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_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);
- }
+ /* The value is larger than 32 bits. */
+
+ if (!dbl || GPR_SIZE == 32)
+ {
+ 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);
+ 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);
+ }
+
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
+
+ if (hi32.X_add_number == (offsetT) 0xffffffff)
+ {
+ if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+ {
+ macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ if (lo32.X_add_number & 0x80000000)
+ {
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ if (lo32.X_add_number & 0xffff)
+ macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
}
}
- else if (mips_pic == VXWORKS_PIC)
- as_bad (_("Non-PIC jump used in PIC library"));
- else
- abort ();
- break;
+ /* 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;
- case M_ACLR_AB:
- ab = 1;
- case M_ACLR_OB:
- s = "aclr";
- treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
- fmt = "\\,~(b)";
- off12 = 1;
- goto ld_st;
- case M_ASET_AB:
- ab = 1;
- case M_ASET_OB:
- s = "aset";
- treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
- fmt = "\\,~(b)";
- off12 = 1;
- goto ld_st;
- case M_LB_AB:
- ab = 1;
- s = "lb";
- fmt = "t,o(b)";
- goto ld;
- case M_LBU_AB:
- ab = 1;
- s = "lbu";
- fmt = "t,o(b)";
- goto ld;
- case M_LH_AB:
- ab = 1;
- s = "lh";
- fmt = "t,o(b)";
- goto ld;
- case M_LHU_AB:
- ab = 1;
- s = "lhu";
- fmt = "t,o(b)";
- goto ld;
- case M_LW_AB:
- ab = 1;
- s = "lw";
- fmt = "t,o(b)";
- goto ld;
- case M_LWC0_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "lwc0";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC1_AB:
- ab = 1;
- s = "lwc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC2_AB:
- ab = 1;
- case M_LWC2_OB:
- s = "lwc2";
- fmt = COP12_FMT;
- off12 = mips_opts.micromips;
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "lwc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWL_AB:
- ab = 1;
- case M_LWL_OB:
- s = "lwl";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_LWR_AB:
- ab = 1;
- case M_LWR_OB:
- s = "lwr";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_LDC1_AB:
- ab = 1;
- s = "ldc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDC2_AB:
- ab = 1;
- case M_LDC2_OB:
- s = "ldc2";
- fmt = COP12_FMT;
- off12 = mips_opts.micromips;
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDC3_AB:
- ab = 1;
- s = "ldc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDL_AB:
- ab = 1;
- case M_LDL_OB:
- s = "ldl";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_LDR_AB:
- ab = 1;
- case M_LDR_OB:
- s = "ldr";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_LL_AB:
- ab = 1;
- case M_LL_OB:
- s = "ll";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld;
- case M_LLD_AB:
- ab = 1;
- case M_LLD_OB:
- s = "lld";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld;
- case M_LWU_AB:
- ab = 1;
- case M_LWU_OB:
- s = "lwu";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld;
- case M_LWP_AB:
- ab = 1;
- case M_LWP_OB:
- gas_assert (mips_opts.micromips);
- s = "lwp";
- fmt = "t,~(b)";
- off12 = 1;
- lp = 1;
- goto ld;
- case M_LDP_AB:
- ab = 1;
- case M_LDP_OB:
- gas_assert (mips_opts.micromips);
- s = "ldp";
- fmt = "t,~(b)";
- off12 = 1;
- lp = 1;
- goto ld;
- case M_LWM_AB:
- ab = 1;
- case M_LWM_OB:
- gas_assert (mips_opts.micromips);
- s = "lwm";
- fmt = "n,~(b)";
- off12 = 1;
- goto ld_st;
- case M_LDM_AB:
- ab = 1;
- case M_LDM_OB:
- gas_assert (mips_opts.micromips);
- s = "ldm";
- fmt = "n,~(b)";
- off12 = 1;
- goto ld_st;
+ 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;
- ld:
- if (breg == treg + lp)
- goto ld_st;
- else
- tempreg = treg + lp;
- goto ld_noat;
+ 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 M_SB_AB:
- ab = 1;
- s = "sb";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SH_AB:
- ab = 1;
- s = "sh";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SW_AB:
- ab = 1;
- s = "sw";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SWC0_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "swc0";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC1_AB:
- ab = 1;
- s = "swc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC2_AB:
- ab = 1;
- case M_SWC2_OB:
- s = "swc2";
- fmt = COP12_FMT;
- off12 = mips_opts.micromips;
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "swc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWL_AB:
- ab = 1;
- case M_SWL_OB:
- s = "swl";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SWR_AB:
- ab = 1;
- case M_SWR_OB:
- s = "swr";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SC_AB:
- ab = 1;
- case M_SC_OB:
- s = "sc";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SCD_AB:
- ab = 1;
- case M_SCD_OB:
- s = "scd";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_CACHE_AB:
- ab = 1;
- case M_CACHE_OB:
- s = "cache";
- fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_PREF_AB:
- ab = 1;
- case M_PREF_OB:
- s = "pref";
- fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SDC1_AB:
- ab = 1;
- s = "sdc1";
- fmt = "T,o(b)";
- coproc = 1;
- /* Itbl support may require additional care here. */
- goto ld_st;
- case M_SDC2_AB:
- ab = 1;
- case M_SDC2_OB:
- s = "sdc2";
- fmt = COP12_FMT;
- off12 = mips_opts.micromips;
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SDC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "sdc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SDL_AB:
- ab = 1;
- case M_SDL_OB:
- s = "sdl";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SDR_AB:
- ab = 1;
- case M_SDR_OB:
- s = "sdr";
- fmt = MEM12_FMT;
- off12 = mips_opts.micromips;
- goto ld_st;
- case M_SWP_AB:
- ab = 1;
- case M_SWP_OB:
- gas_assert (mips_opts.micromips);
- s = "swp";
- fmt = "t,~(b)";
- off12 = 1;
- goto ld_st;
- case M_SDP_AB:
- ab = 1;
- case M_SDP_OB:
- gas_assert (mips_opts.micromips);
- s = "sdp";
- fmt = "t,~(b)";
- off12 = 1;
- goto ld_st;
- case M_SWM_AB:
- ab = 1;
- case M_SWM_OB:
- gas_assert (mips_opts.micromips);
- s = "swm";
- fmt = "n,~(b)";
- off12 = 1;
- goto ld_st;
- case M_SDM_AB:
- ab = 1;
- case M_SDM_OB:
- gas_assert (mips_opts.micromips);
- s = "sdm";
- fmt = "n,~(b)";
- off12 = 1;
-
- ld_st:
- tempreg = AT;
- used_at = 1;
- ld_noat:
- if (coproc
- && NO_ISA_COP (mips_opts.arch)
- && (ip->insn_mo->pinfo2 & (INSN2_M_FP_S | INSN2_M_FP_D)) == 0)
+ /* 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)
{
- as_bad (_("Opcode not supported on this processor: %s"),
- mips_cpu_info_from_arch (mips_opts.arch)->name);
- break;
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
}
-
- if (offset_expr.X_op != O_constant
- && offset_expr.X_op != O_symbol)
+ else
{
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
}
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ /* Optimize if the shifted value is a (power of 2) - 1. */
+ if ((hi == 0 && ((lo + 1) & lo) == 0)
+ || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
{
- char value [32];
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
+ {
+ expressionS tmp;
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ /* 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;
+ }
}
- /* A constant expression in PIC code can be handled just as it
- is in non PIC code. */
- if (offset_expr.X_op == O_constant)
+ /* 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)
{
- int hipart = 0;
-
- expr1.X_add_number = offset_expr.X_add_number;
- normalize_address_expr (&expr1);
- if (!off12 && !IS_SEXT_16BIT_NUM (expr1.X_add_number))
- {
- expr1.X_add_number = ((expr1.X_add_number + 0x8000)
- & ~(bfd_vma) 0xffff);
- hipart = 1;
- }
- else if (off12 && !IS_SEXT_12BIT_NUM (expr1.X_add_number))
- {
- expr1.X_add_number = ((expr1.X_add_number + 0x800)
- & ~(bfd_vma) 0xfff);
- hipart = 1;
- }
- if (hipart)
- {
- load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- breg = tempreg;
- }
- if (!off12)
- macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
- else
- macro_build (NULL, s, fmt,
- treg, (unsigned long) offset_expr.X_add_number, breg);
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+ freg = reg;
}
- else if (off12)
+ }
+ else
+ {
+ expressionS mid16;
+
+ if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
{
- /* A 12-bit offset field is too narrow to be used for a low-part
- relocation, so load the whole address into the auxillary
- register. In the case of "A(b)" addresses, we first load
- absolute address "A" into the register and then add base
- register "b". In the case of "o(b)" addresses, we simply
- need to add 16-bit offset "o" to base register "b", and
- offset_reloc already contains the relocations associated
- with "o". */
- if (ab)
- {
- load_address (tempreg, &offset_expr, &used_at);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- }
- else
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, breg, -1,
- offset_reloc[0], offset_reloc[1], offset_reloc[2]);
- expr1.X_add_number = 0;
- macro_build (NULL, s, fmt,
- treg, (unsigned long) expr1.X_add_number, tempreg);
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+ return;
}
- else if (mips_pic == NO_PIC)
+
+ if (freg != 0)
{
- /* 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.
+ macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+ freg = reg;
+ }
+ mid16 = lo32;
+ mid16.X_add_number >>= 16;
+ macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff) != 0)
+ macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
- 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.
+static inline void
+load_delay_nop (void)
+{
+ if (!gpr_interlocks)
+ macro_build (NULL, "nop", "");
+}
- 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)
+/* Load an address into a register. */
- 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)
+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;
+ }
- 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 (ep->X_op == O_constant)
+ {
+ load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+ return;
+ }
- 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_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $reg,<sym> (BFD_RELOC_HI16_S)
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If we have an addend, we always use the latter form.
- if (mips_relax.sequence)
- relax_end ();
- break;
+ 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)
+ {
+ 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 (breg == 0)
+ if (*used_at == 0 && mips_opts.at)
{
- 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 ();
+ 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
{
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, breg, mips_gp_register);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_GPREL16, tempreg);
- relax_switch ();
- }
- macro_build_lui (&offset_expr, tempreg);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_LO16, tempreg);
- if (mips_relax.sequence)
- relax_end ();
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
}
+
+ if (mips_relax.sequence)
+ relax_end ();
}
- else if (!mips_big_got)
+ else
{
- 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)
+ 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;
- For NewABI, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
+ /* If this is a reference to an external symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ Otherwise we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
- If 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)
+ 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)
{
- 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;
+ 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 ();
}
- 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);
+ 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 (offset_expr.X_add_symbol);
+ relax_start (ep->X_add_symbol);
relax_switch ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
- tempreg, BFD_RELOC_LO16);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ 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);
+
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ }
}
- else if (mips_big_got && !HAVE_NEWABI)
- {
- int gpdelay;
+ }
+ else if (mips_big_got)
+ {
+ expressionS ex;
- /* 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 ();
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $reg,$reg,$gp
+ lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
- if (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)
+ 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.
+
+ 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)
{
- /* 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)
+ 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)"));
- 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);
+ 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);
+ }
+ ep->X_add_number = ex.X_add_number;
relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
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);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_GOT_OFST);
relax_end ();
}
else
- abort ();
-
- 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
+ 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))
{
- 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);
- }
- }
+ /* 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", "");
}
- 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,
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
BFD_RELOC_MIPS_GOT16, mips_gp_register);
- used_at = 1;
- }
+ load_delay_nop ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
- /* 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)
+ if (ex.X_add_number != 0)
{
- /* FIXME: How in the world do we deal with the possible
- overflow here? */
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "lw", "t,o(b)",
- treg + 1, BFD_RELOC_LO16, AT);
+ if (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_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;
- }
+ if (!mips_opts.at && *used_at == 1)
+ as_bad (_("macro used $at after \".set noat\""));
+}
- 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)
- {
- if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
- {
- macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
- BFD_RELOC_MIPS_LITERAL, mips_gp_register);
- break;
- }
- breg = mips_gp_register;
- r = BFD_RELOC_MIPS_LITERAL;
- goto dob;
- }
- 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);
- }
-
- if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
- {
- macro_build (&offset_expr, "ldc1", "T,o(b)",
- treg, BFD_RELOC_LO16, AT);
- break;
- }
- breg = AT;
- r = BFD_RELOC_LO16;
- goto dob;
- }
+/* Move the contents of register SOURCE into register DEST. */
- case M_L_DOB:
- /* Even on a big endian machine $fn comes before $fn+1. We have
- to adjust when loading from memory. */
- r = BFD_RELOC_LO16;
- dob:
- gas_assert (!mips_opts.micromips);
- gas_assert (mips_opts.isa == ISA_MIPS1);
- macro_build (&offset_expr, "lwc1", "T,o(b)",
- target_big_endian ? treg + 1 : treg, r, breg);
- /* FIXME: A possible overflow which I don't know how to deal
- with. */
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "lwc1", "T,o(b)",
- target_big_endian ? treg : treg + 1, r, breg);
- break;
+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, "or", "d,v,t", dest, source, 0);
+}
- case M_S_DOB:
- gas_assert (!mips_opts.micromips);
- gas_assert (mips_opts.isa == ISA_MIPS1);
- /* Even on a big endian machine $fn comes before $fn+1. We have
- to adjust when storing to memory. */
- macro_build (&offset_expr, "swc1", "T,o(b)",
- target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "swc1", "T,o(b)",
- target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
- break;
+/* 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_L_DAB:
- gas_assert (!mips_opts.micromips);
- /*
- * The MIPS assembler seems to check for X_add_number not
- * being double aligned and generating:
- * lui at,%hi(foo+1)
- * addu at,at,v1
- * addiu at,at,%lo(foo+1)
- * lwc1 f2,0(at)
- * lwc1 f3,4(at)
- * But, the resulting address is the same after relocation so why
- * generate the extra instruction?
- */
- /* Itbl support may require additional care here. */
- coproc = 1;
- fmt = "T,o(b)";
- if (mips_opts.isa != ISA_MIPS1)
- {
- s = "ldc1";
- goto ld_st;
- }
- s = "lwc1";
- goto ldd_std;
+ Global symbol Local sybmol
+ ------------- ------------
+ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
+ ... ...
+ addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
- case M_S_DAB:
- gas_assert (!mips_opts.micromips);
- /* Itbl support may require additional care here. */
- coproc = 1;
- fmt = "T,o(b)";
- if (mips_opts.isa != ISA_MIPS1)
- {
- s = "sdc1";
- goto ld_st;
- }
- s = "swc1";
- goto ldd_std;
+ 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. */
- case M_LD_AB:
- fmt = "t,o(b)";
- if (HAVE_64BIT_GPRS)
- {
- s = "ld";
- goto ld;
- }
- s = "lw";
- goto ldd_std;
+static void
+load_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- case M_SD_AB:
- fmt = "t,o(b)";
- if (HAVE_64BIT_GPRS)
- {
- s = "sd";
- goto ld_st;
- }
- s = "sw";
+ global = *local;
+ global.X_add_number = 0;
- ldd_std:
- if (offset_expr.X_op != O_symbol
- && offset_expr.X_op != O_constant)
- {
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
- }
+ 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 ();
+}
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
- {
- char value [32];
+static void
+add_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
- }
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- /* 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;
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+ dest, dest, BFD_RELOC_LO16);
+ relax_switch ();
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+ relax_end ();
+}
- if (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;
- }
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+ expressionS global;
+ int hold_mips_optimize;
- /* 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;
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- /* 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_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 ();
- relax_switch ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
- offset_expr.X_add_number -= 4;
- }
- used_at = 1;
- macro_build_lui (&offset_expr, AT);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- /* FIXME: How do we handle overflow here? */
- offset_expr.X_add_number += 4;
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- if (mips_relax.sequence)
- relax_end ();
- }
- else if (!mips_big_got)
- {
- /* If this is a reference to an external symbol, we want
- lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- <op> $treg,0($at)
- <op> $treg+1,4($at)
- Otherwise we want
- lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- <op> $treg,<sym>($at) (BFD_RELOC_LO16)
- <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
- If there is a base register we add it to $at before the
- lwc1 instructions. If there is a constant we include it
- in the lwc1 instructions. */
- used_at = 1;
- expr1.X_add_number = offset_expr.X_add_number;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000 - 4)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- load_got_offset (AT, &offset_expr);
- load_delay_nop ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+/* 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.
- /* Set mips_optimize to 2 to avoid inserting an undesired
- nop. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
+ Code like this is produced in the noreorder mode:
- /* 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 ();
+ BRNEG <args>, 1f
+ nop
+ b <sym>
+ delay slot (executed only if branch taken)
+ 1:
- mips_optimize = hold_mips_optimize;
- }
- else if (mips_big_got)
- {
- int gpdelay;
+ or, if CALL is set:
- /* 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;
+ BRNEG <args>, 1f
+ nop
+ bal <sym>
+ delay slot (executed only if branch taken)
+ 1:
- 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;
+ In the reorder mode the delay slot would be filled with a nop anyway,
+ so code produced is simply:
- /* 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 ();
+ BR <args>, <sym>
+ nop
- break;
+ This function is used when producing code for the microMIPS ASE that
+ does not implement branch likely instructions in hardware. */
- case M_LD_OB:
- s = HAVE_64BIT_GPRS ? "ld" : "lw";
- goto sd_ob;
- case M_SD_OB:
- s = HAVE_64BIT_GPRS ? "sd" : "sw";
- sd_ob:
- macro_build (&offset_expr, s, "t,o(b)", treg,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
- breg);
- if (!HAVE_64BIT_GPRS)
- {
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, s, "t,o(b)", treg + 1,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
- breg);
- }
- break;
+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;
- /* 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.
+ 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");
- 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?
+ /* 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 an itbl is provided to interpret cop instructions,
- this knowledge can be encoded in the itbl spec. */
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+ the condition code tested. EP specifies the branch target. */
- 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);
- if (NO_ISA_COP (mips_opts.arch)
- && (ip->insn_mo->pinfo2 & INSN2_M_FP_S) == 0)
- {
- as_bad (_("Opcode not supported on this processor: %s"),
- mips_cpu_info_from_arch (mips_opts.arch)->name);
- break;
- }
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+ const int call = 0;
+ const char *brneg;
+ const char *br;
- /* For now we just do C (same as Cz). The parameter will be
- stored in insn_opcode by mips_ip. */
- macro_build (NULL, s, "C", ip->insn_opcode);
+ switch (type)
+ {
+ case M_BC1FL:
+ br = "bc1f";
+ brneg = "bc1t";
break;
-
- case M_MOVE:
- move_register (dreg, sreg);
+ case M_BC1TL:
+ br = "bc1t";
+ brneg = "bc1f";
break;
-
- case M_DMUL:
- dbl = 1;
- case M_MUL:
- macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ case M_BC2FL:
+ br = "bc2f";
+ brneg = "bc2t";
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);
+ case M_BC2TL:
+ br = "bc2t";
+ brneg = "bc2f";
break;
+ default:
+ abort ();
+ }
+ macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
+}
- 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;
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+ the register tested. EP specifies the branch target. */
- case M_DMULOU_I:
- dbl = 1;
- case M_MULOU_I:
- imm = 1;
- goto do_mulou;
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+ const char *brneg = NULL;
+ const char *br;
+ int call = 0;
- case M_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 ();
+ switch (type)
+ {
+ case M_BGEZ:
+ br = "bgez";
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);
+ case M_BGEZL:
+ br = mips_opts.micromips ? "bgez" : "bgezl";
+ brneg = "bltz";
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);
+ case M_BGEZALL:
+ gas_assert (mips_opts.micromips);
+ br = mips_opts.insn32 ? "bgezal" : "bgezals";
+ brneg = "bltz";
+ call = 1;
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);
- }
+ case M_BGTZ:
+ br = "bgtz";
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);
- }
+ case M_BGTZL:
+ br = mips_opts.micromips ? "bgtz" : "bgtzl";
+ brneg = "blez";
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);
+ case M_BLEZ:
+ br = "blez";
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);
+ case M_BLEZL:
+ br = mips_opts.micromips ? "blez" : "blezl";
+ brneg = "bgtz";
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);
- }
+ case M_BLTZ:
+ br = "bltz";
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);
- }
+ case M_BLTZL:
+ br = mips_opts.micromips ? "bltz" : "bltzl";
+ brneg = "bgez";
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);
- }
+ 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);
+}
- 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;
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+ TREG as the registers tested. EP specifies the branch target. */
- 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;
+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;
- 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);
+ switch (type)
+ {
+ case M_BEQ:
+ case M_BEQ_I:
+ br = "beq";
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);
+ case M_BEQL:
+ case M_BEQL_I:
+ br = mips_opts.micromips ? "beq" : "beql";
+ brneg = "bne";
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);
+ case M_BNE:
+ case M_BNE_I:
+ br = "bne";
break;
-
- case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */
- s = "slt";
- goto sle;
- case M_SLEU:
- s = "sltu";
- sle:
- macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
- macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ case M_BNEL:
+ case M_BNEL_I:
+ br = mips_opts.micromips ? "bne" : "bnel";
+ brneg = "beq";
break;
+ default:
+ abort ();
+ }
+ if (mips_opts.micromips && brneg)
+ macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
+ else
+ macro_build (ep, br, "s,t,p", sreg, treg);
+}
- case M_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;
+/* Return the high part that should be loaded in order to make the low
+ part of VALUE accessible using an offset of OFFBITS bits. */
- case M_SLT_I:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
+static offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+ offsetT bias;
+ addressT low_mask;
+
+ if (offbits == 0)
+ return value;
+ bias = 1 << (offbits - 1);
+ low_mask = bias * 2 - 1;
+ return (value + bias) & ~low_mask;
+}
+
+/* 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. */
+
+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;
+
+ /* 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;
+}
+
+/*
+ * 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 omitted. 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];
+
+ gas_assert (! mips_opts.mips16);
+
+ 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;
+
+ 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;
+ /* Fall through. */
+ 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", 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 ();
+
+ 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_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, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ 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, HAVE_64BIT_GPRS);
- macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], 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)
+ 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)
{
- macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
- BFD_RELOC_LO16);
+ if (mask != M_NOR_I)
+ macro_build (&imm_expr, s, "t,r,i", op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ 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);
+ }
break;
}
+
used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], 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
+ case M_BALIGN:
+ switch (imm_expr.X_add_number)
{
- macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ 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_SNE_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ 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_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
{
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
- break;
+ op[1] = AT;
+ used_at = 1;
+ load_register (op[1], &imm_expr, GPR_SIZE == 64);
}
- if (sreg == 0)
+ /* Fall through. */
+ case M_BEQL:
+ case M_BNEL:
+ macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]);
+ break;
+
+ case M_BGEL:
+ likely = 1;
+ /* Fall through. */
+ 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
{
- 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;
+ 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);
}
- if (CPU_HAS_SEQ (mips_opts.arch)
- && -512 <= imm_expr.X_add_number
- && imm_expr.X_add_number < 512)
+ break;
+
+ case M_BGEZL:
+ case M_BGEZALL:
+ case M_BGTZL:
+ case M_BLEZL:
+ case M_BLTZL:
+ case M_BLTZALL:
+ macro_build_branch_rs (mask, &offset_expr, op[0]);
+ break;
+
+ case M_BGTL_I:
+ likely = 1;
+ /* Fall through. */
+ case M_BGT_I:
+ /* Check for > max integer. */
+ if (imm_expr.X_add_number >= GPR_SMAX)
{
- macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
- (int) imm_expr.X_add_number);
+ do_false:
+ /* Result is always false. */
+ if (! likely)
+ macro_build (NULL, "nop", "");
+ else
+ macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
break;
}
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= 0
- && imm_expr.X_add_number < 0x10000)
+ ++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)
{
- macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
+ &offset_expr, op[0]);
+ break;
}
- else if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x8000
- && imm_expr.X_add_number < 0)
+ if (imm_expr.X_add_number == 1)
{
- 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);
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
+ &offset_expr, op[0]);
+ break;
}
- else if (CPU_HAS_SEQ (mips_opts.arch))
+ if (imm_expr.X_add_number <= GPR_SMIN)
{
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
+ 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;
+
+ case M_BGEUL:
+ likely = 1;
+ /* Fall through. */
+ 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
{
- 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", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
}
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
break;
- case M_SUB_I:
- s = "addi";
- s2 = "sub";
- goto do_subi;
- case M_SUBU_I:
- s = "addiu";
- s2 = "subu";
- goto do_subi;
- case M_DSUB_I:
- dbl = 1;
- s = "daddi";
- s2 = "dsub";
- if (!mips_opts.micromips)
- goto do_subi;
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x200
- && imm_expr.X_add_number <= 0x200)
- {
- macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
- break;
- }
- goto do_subi_i;
- case M_DSUBU_I:
- dbl = 1;
- s = "daddiu";
- s2 = "dsubu";
- do_subi:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x8000
- && imm_expr.X_add_number <= 0x8000)
+ case M_BGTUL_I:
+ likely = 1;
+ /* Fall through. */
+ 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
{
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- break;
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
}
- 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_A:
- ab = 1;
- case M_ULH:
- s = "lb";
- s2 = "lbu";
- off = 1;
- goto uld_st;
- case M_ULHU_A:
- ab = 1;
- case M_ULHU:
- s = "lbu";
- s2 = "lbu";
- off = 1;
- goto uld_st;
- case M_ULW_A:
- ab = 1;
- case M_ULW:
- s = "lwl";
- s2 = "lwr";
- off12 = mips_opts.micromips;
- off = 3;
- goto uld_st;
- case M_ULD_A:
- ab = 1;
- case M_ULD:
- s = "ldl";
- s2 = "ldr";
- off12 = mips_opts.micromips;
- off = 7;
- goto uld_st;
- case M_USH_A:
- ab = 1;
- case M_USH:
- s = "sb";
- s2 = "sb";
- off = 1;
- ust = 1;
- goto uld_st;
- case M_USW_A:
- ab = 1;
- case M_USW:
- s = "swl";
- s2 = "swr";
- off12 = mips_opts.micromips;
- off = 3;
- ust = 1;
- goto uld_st;
- case M_USD_A:
- ab = 1;
- case M_USD:
- s = "sdl";
- s2 = "sdr";
- off12 = mips_opts.micromips;
- off = 7;
- ust = 1;
-
- uld_st:
- if (!ab && offset_expr.X_add_number >= 0x8000 - off)
- as_bad (_("Operand overflow"));
-
- ep = &offset_expr;
- expr1.X_add_number = 0;
- if (ab)
+ case M_BGTL:
+ likely = 1;
+ /* Fall through. */
+ 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;
- tempreg = AT;
- load_address (tempreg, ep, &used_at);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- breg = tempreg;
- tempreg = treg;
- ep = &expr1;
+ 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);
}
- else if (off12
- && (offset_expr.X_op != O_constant
- || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number)
- || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number + off)))
+ break;
+
+ case M_BGTUL:
+ likely = 1;
+ /* Fall through. */
+ 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
{
used_at = 1;
- tempreg = AT;
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
- breg = tempreg;
- tempreg = treg;
- ep = &expr1;
+ 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);
}
- else if (!ust && treg == breg)
+ break;
+
+ case M_BLEL:
+ likely = 1;
+ /* Fall through. */
+ 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
{
used_at = 1;
- tempreg = AT;
+ 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);
}
- else
- tempreg = treg;
-
- if (off == 1)
- goto ulh_sh;
+ break;
- if (!target_big_endian)
- ep->X_add_number += off;
- if (!off12)
- macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+ case M_BLEL_I:
+ likely = 1;
+ /* Fall through. */
+ 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
- macro_build (NULL, s, "t,~(b)",
- tempreg, (unsigned long) ep->X_add_number, breg);
+ {
+ used_at = 1;
+ set_at (op[0], 0);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- if (!target_big_endian)
- ep->X_add_number -= off;
- else
- ep->X_add_number += off;
- if (!off12)
- macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+ case M_BLEUL:
+ likely = 1;
+ /* Fall through. */
+ 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
- macro_build (NULL, s2, "t,~(b)",
- tempreg, (unsigned long) ep->X_add_number, breg);
-
- /* 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);
+ {
+ 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;
- ulh_sh:
- used_at = 1;
- if (target_big_endian == ust)
- ep->X_add_number += off;
- tempreg = ust || ab ? treg : AT;
- macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
-
- /* For halfword transfers we need a temporary register to shuffle
- bytes. Unfortunately for M_USH_A we have none available before
- the next store as AT holds the base address. We deal with this
- case by clobbering TREG and then restoring it as with ULH. */
- tempreg = ust == ab ? treg : AT;
- if (ust)
- macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
-
- if (target_big_endian == ust)
- ep->X_add_number -= off;
+ case M_BLEUL_I:
+ likely = 1;
+ /* Fall through. */
+ 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
- ep->X_add_number += off;
- macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
-
- /* For M_USH_A re-retrieve the LSB. */
- if (ust && ab)
{
- if (target_big_endian)
- ep->X_add_number += off;
- else
- ep->X_add_number -= off;
- macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
- /* For ULH and M_USH_A OR the LSB in. */
- if (!ust || ab)
+ break;
+
+ case M_BLTL:
+ likely = 1;
+ /* Fall through. */
+ 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
{
- tempreg = !ab ? AT : treg;
- macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
- macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ 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);
}
break;
- default:
- /* FIXME: Check if this is one of the itbl macros, since they
- are added dynamically. */
- as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
- break;
- }
- if (!mips_opts.at && used_at)
- as_bad (_("Macro used $at after \".set noat\""));
-}
-
-/* Implement macros in mips16 mode. */
-
-static void
-mips16_macro (struct mips_cl_insn *ip)
-{
- int mask;
- int xreg, yreg, zreg, tmp;
- expressionS expr1;
- int dbl;
- const char *s, *s2, *s3;
-
- mask = ip->insn_mo->mask;
-
- xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
- yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
- zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
-
- expr1.X_op = O_constant;
- expr1.X_op_symbol = NULL;
- expr1.X_add_symbol = NULL;
- expr1.X_add_number = 1;
-
- dbl = 0;
-
- switch (mask)
- {
- default:
- internalError ();
+ case M_BLTUL:
+ likely = 1;
+ /* Fall through. */
+ 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
+ {
+ 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;
+ /* Fall through. */
case M_DIV_3:
s = "mflo";
goto do_div3;
case M_DREM_3:
dbl = 1;
+ /* Fall through. */
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 ();
- 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_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", "");
- /* 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);
+ /* 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_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_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_add_number == 1)
+ {
+ if (strcmp (s2, "mflo") == 0)
+ move_register (op[0], op[1]);
+ else
+ move_register (op[0], ZERO);
+ break;
+ }
+ if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u')
+ {
+ if (strcmp (s2, "mflo") == 0)
+ macro_build (NULL, dbl ? "dneg" : "neg", "d,w", op[0], op[1]);
+ else
+ move_register (op[0], ZERO);
+ break;
+ }
+
+ 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;
case M_DIVU_3:
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 (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
+ {
+ 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]);
- case M_DMUL:
- dbl = 1;
- case M_MUL:
- macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
- macro_build (NULL, "mflo", "x", zreg);
+ /* 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;
- case M_DSUBU_I:
+ case M_DLCA_AB:
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;
+ /* Fall through. */
+ case M_LCA_AB:
+ call = 1;
+ goto do_la;
+ case M_DLA_AB:
+ dbl = 1;
+ /* Fall through. */
+ case M_LA_AB:
+ do_la:
+ /* Load the address of a symbol into a register. If breg is not
+ zero, we then add a base register to it. */
- case M_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;
+ breg = op[2];
+ if (dbl && GPR_SIZE == 32)
+ as_warn (_("dla used to load 32-bit register; recommend using la "
+ "instead"));
- 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 (!dbl && HAVE_64BIT_OBJECTS)
+ as_warn (_("la used to load 64-bit address; recommend using dla "
+ "instead"));
- 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";
-
- do_reverse_branch:
- tmp = xreg;
- xreg = yreg;
- yreg = tmp;
+ if (small_offset_p (0, align, 16))
+ {
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", op[0], breg,
+ -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ break;
+ }
- do_branch:
- macro_build (NULL, s, "x,y", xreg, yreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ if (mips_opts.at && (op[0] == breg))
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
- 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_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- do_addone_branch_i:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
+ if (offset_expr.X_op == O_constant)
+ load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+ else if (mips_pic == NO_PIC)
+ {
+ /* 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.
- do_branch_i:
- macro_build (&imm_expr, s, s3, xreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ 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
- 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 $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_LO16)
-/* For consistency checking, verify that all bits are specified either
- by the match/mask part of the instruction definition, or by the
- operand list. */
-static int
-validate_mips_insn (const struct mips_opcode *opc)
-{
- const char *p = opc->args;
- char c;
- unsigned long used_bits = opc->mask;
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
- if ((used_bits & opc->match) != opc->match)
- {
- as_bad (_("internal: bad mips opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
-#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- switch (c = *p++)
- {
- case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break;
- case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break;
- case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break;
- case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break;
- case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD);
- USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'I': break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'T': USE_BITS (OP_MASK_RT, OP_SH_RT);
- USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'x': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'X': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'p': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'P': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'Q': USE_BITS (OP_MASK_SEQI, OP_SH_SEQI); break;
- case 's': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'S': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'z': USE_BITS (OP_MASK_RZ, OP_SH_RZ); break;
- case 'Z': USE_BITS (OP_MASK_FZ, OP_SH_FZ); break;
- case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break;
- case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break;
- case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break;
-
- default:
- as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
- break;
- case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'A': break;
- case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break;
- case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break;
- case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'F': break;
- case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'I': break;
- case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break;
- case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'L': break;
- case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break;
- case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break;
- case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break;
- case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL);
- USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
- case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
- case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break;
- case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'f': break;
- case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break;
- case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break;
- case 'l': break;
- case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break;
- case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'x': break;
- case 'z': break;
- case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break;
- case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD);
- USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break;
- case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break;
- case '[': break;
- case ']': break;
- case '1': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break;
- case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break;
- case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break;
- case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break;
- case '6': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case '7': USE_BITS (OP_MASK_DSPACC, OP_SH_DSPACC); break;
- case '8': USE_BITS (OP_MASK_WRDSP, OP_SH_WRDSP); break;
- case '9': USE_BITS (OP_MASK_DSPACC_S, OP_SH_DSPACC_S);break;
- case '0': USE_BITS (OP_MASK_DSPSFT, OP_SH_DSPSFT); break;
- case '\'': USE_BITS (OP_MASK_RDDSP, OP_SH_RDDSP); break;
- case ':': USE_BITS (OP_MASK_DSPSFT_7, OP_SH_DSPSFT_7);break;
- case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break;
- case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break;
- case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break;
- case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break;
- case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break;
- case '\\': USE_BITS (OP_MASK_3BITPOS, OP_SH_3BITPOS); break;
- case '~': USE_BITS (OP_MASK_OFFSET12, OP_SH_OFFSET12); break;
- case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- default:
- as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != 0xffffffff)
- {
- as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
- ~used_bits & 0xffffffff, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
+ if (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);
+ }
-/* For consistency checking, verify that the length implied matches the
- major opcode and that all bits are specified either by the match/mask
- part of the instruction definition, or by the operand list. */
+ if (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;
-static int
-validate_micromips_insn (const struct mips_opcode *opc)
-{
- unsigned long match = opc->match;
- unsigned long mask = opc->mask;
- const char *p = opc->args;
- unsigned long insn_bits;
- unsigned long used_bits;
- unsigned long major;
- unsigned int length;
- char e;
- char c;
+ /* If this is a reference to an external symbol, and there
+ is no constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
- if ((mask & match) != match)
- {
- as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
- length = micromips_insn_length (opc);
- if (length != 2 && length != 4)
- {
- as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
- "%s %s"), length, opc->name, opc->args);
- return 0;
- }
- major = match >> (10 + 8 * (length - 2));
- if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
- || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
- {
- as_bad (_("Internal error: bad microMIPS opcode "
- "(opcode/length mismatch): %s %s"), opc->name, opc->args);
- return 0;
- }
+ 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.
- /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
- insn_bits = 1 << 4 * length;
- insn_bits <<= 4 * length;
- insn_bits -= 1;
- used_bits = mask;
-#define USE_BITS(field) \
- (used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field)
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (EXTLSB); break;
- case 'B': USE_BITS (INSMSB); break;
- case 'C': USE_BITS (EXTMSBD); break;
- case 'D': USE_BITS (RS); USE_BITS (SEL); break;
- case 'E': USE_BITS (EXTLSB); break;
- case 'F': USE_BITS (INSMSB); break;
- case 'G': USE_BITS (EXTMSBD); break;
- case 'H': USE_BITS (EXTMSBD); break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case 'm':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (IMMA); break;
- case 'B': USE_BITS (IMMB); break;
- case 'C': USE_BITS (IMMC); break;
- case 'D': USE_BITS (IMMD); break;
- case 'E': USE_BITS (IMME); break;
- case 'F': USE_BITS (IMMF); break;
- case 'G': USE_BITS (IMMG); break;
- case 'H': USE_BITS (IMMH); break;
- case 'I': USE_BITS (IMMI); break;
- case 'J': USE_BITS (IMMJ); break;
- case 'L': USE_BITS (IMML); break;
- case 'M': USE_BITS (IMMM); break;
- case 'N': USE_BITS (IMMN); break;
- case 'O': USE_BITS (IMMO); break;
- case 'P': USE_BITS (IMMP); break;
- case 'Q': USE_BITS (IMMQ); break;
- case 'U': USE_BITS (IMMU); break;
- case 'W': USE_BITS (IMMW); break;
- case 'X': USE_BITS (IMMX); break;
- case 'Y': USE_BITS (IMMY); break;
- case 'Z': break;
- case 'a': break;
- case 'b': USE_BITS (MB); break;
- case 'c': USE_BITS (MC); break;
- case 'd': USE_BITS (MD); break;
- case 'e': USE_BITS (ME); break;
- case 'f': USE_BITS (MF); break;
- case 'g': USE_BITS (MG); break;
- case 'h': USE_BITS (MH); break;
- case 'i': USE_BITS (MI); break;
- case 'j': USE_BITS (MJ); break;
- case 'l': USE_BITS (ML); break;
- case 'm': USE_BITS (MM); break;
- case 'n': USE_BITS (MN); break;
- case 'p': USE_BITS (MP); break;
- case 'q': USE_BITS (MQ); break;
- case 'r': break;
- case 's': break;
- case 't': break;
- case 'x': break;
- case 'y': break;
- case 'z': break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case '.': USE_BITS (OFFSET10); break;
- case '1': USE_BITS (STYPE); break;
- case '<': USE_BITS (SHAMT); break;
- case '>': USE_BITS (SHAMT); break;
- case 'B': USE_BITS (CODE10); break;
- case 'C': USE_BITS (COPZ); break;
- case 'D': USE_BITS (FD); break;
- case 'E': USE_BITS (RT); break;
- case 'G': USE_BITS (RS); break;
- case 'H': USE_BITS (SEL); break;
- case 'K': USE_BITS (RS); break;
- case 'M': USE_BITS (CCC); break;
- case 'N': USE_BITS (BCC); break;
- case 'R': USE_BITS (FR); break;
- case 'S': USE_BITS (FS); break;
- case 'T': USE_BITS (FT); break;
- case 'V': USE_BITS (FS); break;
- case '\\': USE_BITS (3BITPOS); break;
- case 'a': USE_BITS (TARGET); break;
- case 'b': USE_BITS (RS); break;
- case 'c': USE_BITS (CODE); break;
- case 'd': USE_BITS (RD); break;
- case 'h': USE_BITS (PREFX); break;
- case 'i': USE_BITS (IMMEDIATE); break;
- case 'j': USE_BITS (DELTA); break;
- case 'k': USE_BITS (CACHE); break;
- case 'n': USE_BITS (RT); break;
- case 'o': USE_BITS (DELTA); break;
- case 'p': USE_BITS (DELTA); break;
- case 'q': USE_BITS (CODE2); break;
- case 'r': USE_BITS (RS); break;
- case 's': USE_BITS (RS); break;
- case 't': USE_BITS (RT); break;
- case 'u': USE_BITS (IMMEDIATE); break;
- case 'v': USE_BITS (RS); break;
- case 'w': USE_BITS (RT); break;
- case 'y': USE_BITS (RS3); break;
- case 'z': break;
- case '|': USE_BITS (TRAP); break;
- case '~': USE_BITS (OFFSET12); break;
- default:
- as_bad (_("Internal error: bad microMIPS opcode "
- "(unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != insn_bits)
- {
- if (~used_bits & insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx undefined): %s %s"),
- ~used_bits & insn_bits, opc->name, opc->args);
- if (used_bits & ~insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx defined): %s %s"),
- used_bits & ~insn_bits, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
+ 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.
+ */
-/* UDI immediates. */
-struct mips_immed {
- char type;
- unsigned int shift;
- unsigned long mask;
- const char * desc;
-};
+ 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;
+
+ 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;
+
+ /* If this is a reference to an external, and there is no
+ constant, or local symbol (*), with or without a
+ constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+
+ If 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>
+
+ 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
+
+ (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+ local symbols, even though it introduces an additional
+ instruction. */
+
+ if (offset_expr.X_add_number)
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ unsigned int dreg;
+
+ /* 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);
+
+ used_at = 1;
+ }
+ 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_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;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
+ addu $tempreg,$tempreg,$at
+ */
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ relax_start (offset_expr.X_add_symbol);
+ gpdelay = reg_needs_delay (mips_gp_register);
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ 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;
+
+ /* 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];
+ }
+
+ 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 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>
+
+ 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
+
+ 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. */
+
+ relax_start (offset_expr.X_add_symbol);
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ 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)
+ ;
+ 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))
+ {
+ unsigned int dreg;
+
+ /* 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);
+
+ used_at = 1;
+ }
+ 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",
+ op[0], tempreg, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ relax_end ();
+ }
+ else
+ abort ();
+
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg);
+ break;
+
+ case M_MSGSND:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x01);
+ break;
+
+ case M_MSGLD:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 0x02);
+ break;
+
+ case M_MSGLD_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x02);
+ break;
+
+ case M_MSGWAIT:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 3);
+ break;
+
+ case M_MSGWAIT_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x03);
+ break;
+
+ case M_J_A:
+ /* The j instruction may not be used in PIC code, since it
+ requires an absolute address. We convert it to a b
+ instruction. */
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, "j", "a");
+ else
+ macro_build (&offset_expr, "b", "p");
+ 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);
+
+ if (op[1] != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
+
+ s = ((mips_opts.micromips
+ && !mips_opts.insn32
+ && (!mips_opts.noreorder || cprestore))
+ ? "jalrs" : "jalr");
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && 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
+ {
+ 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);
+ }
+ }
+ }
+
+ break;
+
+ 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)
+
+ 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 ();
+ }
+
+ 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;
-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 }
-};
+ 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);
-/* Check whether an odd floating-point register is allowed. */
-static int
-mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum)
-{
- const char *s = insn->name;
+ if (mips_cprestore_offset < 0)
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (!mips_frame_reg_valid)
+ {
+ as_warn (_("no .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (!mips_cprestore_valid)
+ {
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+ else if (mips_pic == VXWORKS_PIC)
+ as_bad (_("non-PIC jump used in PIC library"));
+ else
+ abort ();
- if (insn->pinfo == INSN_MACRO)
- /* Let a macro pass, we'll catch it later when it is expanded. */
- return 1;
+ 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
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 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
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 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 = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld;
+ case M_LLD_AB:
+ s = "lld";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 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 (op[2] == op[0] + lp || op[0] + lp == ZERO)
+ goto ld_st;
+ else
+ tempreg = op[0] + 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
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 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 = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_SCD_AB:
+ s = "scd";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_CACHE_AB:
+ s = "cache";
+ fmt = (mips_opts.micromips ? "k,~(b)"
+ : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+ : "k,o(b)");
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 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,~(b)"
+ : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+ : "k,o(b)");
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 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
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 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;
- if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa))
- {
- /* Allow odd registers for single-precision ops. */
- switch (insn->pinfo & (FP_S | FP_D))
+ ld_st:
+ tempreg = AT;
+ ld_noat:
+ breg = op[2];
+ if (small_offset_p (0, align, 16))
{
- case FP_S:
- case 0:
- return 1; /* both single precision - ok */
- case FP_D:
- return 0; /* both double precision - fail */
- default:
+ /* 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;
}
- /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */
- s = strchr (insn->name, '.');
- if (argnum == 2)
- s = s != NULL ? strchr (s + 1, '.') : NULL;
- return (s != NULL && (s[1] == 'w' || s[1] == 's'));
- }
-
- /* Single-precision coprocessor loads and moves are OK too. */
- if ((insn->pinfo & FP_S)
- && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
- | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
- return 1;
-
- return 0;
-}
-
-/* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
- taking bits from BIT up. */
-static int
-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);
-}
-
-/* This routine assembles an instruction into its binary format. As a
- side effect, it sets one of the global variables imm_reloc or
- offset_reloc to the type of relocation to do if one of the operands
- is an address expression. */
-
-static void
-mips_ip (char *str, struct mips_cl_insn *ip)
-{
- bfd_boolean wrong_delay_slot_insns = FALSE;
- bfd_boolean need_delay_slot_ok = TRUE;
- struct mips_opcode *firstinsn = NULL;
- const struct mips_opcode *past;
- struct hash_control *hash;
- char *s;
- const char *args;
- char c = 0;
- struct mips_opcode *insn;
- char *argsStart;
- unsigned int regno;
- unsigned int lastregno;
- unsigned int destregno = 0;
- unsigned int lastpos = 0;
- unsigned int limlo, limhi;
- char *s_reset;
- offsetT min_range, max_range;
- long opend;
- char *name;
- int argnum;
- unsigned int rtype;
- char *dot;
- long end;
-
- insn_error = NULL;
-
- 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;
-
- /* 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;
-
- /* Make a copy of the instruction so that we can fiddle with it. */
- name = alloca (end + 1);
- memcpy (name, str, end);
- name[end] = '\0';
-
- for (;;)
- {
- insn = (struct mips_opcode *) hash_find (hash, name);
-
- if (insn != NULL || !mips_opts.micromips)
- break;
- if (forced_insn_length)
- break;
-
- /* 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;
- }
-
- /* 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 (tempreg == AT)
+ used_at = 1;
- gas_assert (strcmp (insn->name, name) == 0);
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- 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)
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
{
- firstinsn = insn;
- wrong_delay_slot_insns = TRUE;
+ char value [32];
+
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
}
- if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
+
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (offset_expr.X_op == O_constant)
{
- static char buf[256];
+ 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;
- if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
- {
- ++insn;
- continue;
- }
- if (wrong_delay_slot_insns && need_delay_slot_ok)
+ 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)
{
- gas_assert (firstinsn);
- need_delay_slot_ok = FALSE;
- past = insn + 1;
- insn = firstinsn;
- continue;
+ 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);
}
-
- if (insn_error)
- return;
-
- if (!ok)
- sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
- mips_cpu_info_from_arch (mips_opts.arch)->name,
- mips_cpu_info_from_isa (mips_opts.isa)->name);
+ else if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
else
- sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
- 8 * forced_insn_length);
- insn_error = buf;
-
- return;
+ macro_build (NULL, s, fmt, op[0],
+ (int) offset_expr.X_add_number, tempreg);
}
-
- create_insn (ip, insn);
- insn_error = NULL;
- argnum = 1;
- lastregno = 0xffffffff;
- for (args = insn->args;; ++args)
+ 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 auxiliary
+ 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)
{
- int is_mdmx;
+ /* 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.
+
+ 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.
+
+ 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)
+
+ 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)
- s += strspn (s, " \t");
- is_mdmx = 0;
- switch (*args)
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
{
- case '\0': /* end of args */
- if (*s == '\0')
- return;
- break;
+ 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, op[0],
+ 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, op[0],
+ BFD_RELOC_GPREL16, tempreg);
+ }
+ relax_switch ();
+ }
- case '2': /* DSP 2-bit unsigned immediate in bit 11. */
- gas_assert (!mips_opts.micromips);
- 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)
+ 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, op[0], BFD_RELOC_LO16,
+ tempreg);
+ used_at = 1;
+ }
+ else
{
- as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
- (unsigned long) imm_expr.X_add_number);
+ 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);
}
- INSERT_OPERAND (0, BP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
- case '3': /* DSP 3-bit unsigned immediate in bit 21. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_SA3)
+ 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))
{
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_SA3, (unsigned long) imm_expr.X_add_number);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_GPREL16,
+ mips_gp_register);
+ relax_switch ();
}
- INSERT_OPERAND (0, SA3, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case '4': /* DSP 4-bit unsigned immediate in bit 21. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_SA4)
+ 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))
{
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_SA4, (unsigned long) imm_expr.X_add_number);
+ 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 ();
}
- INSERT_OPERAND (0, SA4, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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;
- case '5': /* DSP 8-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_IMM8)
- {
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, IMM8, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* If this is a reference to an external symbol, we want
+ 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 '6': /* DSP 5-bit unsigned immediate in bit 21. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_RS)
- {
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_RS, (unsigned long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, RS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- 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 '7': /* Four DSP accumulators in bits 11,12. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, DSPACC, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp acc register"));
+ 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 '8': /* DSP 6-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 & ~OP_MASK_WRDSP)
- {
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_WRDSP,
- (unsigned long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, WRDSP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* If this is a reference to an external symbol, we want
+ lui $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 '9': /* Four DSP accumulators in bits 21,22. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, DSPACC_S, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp acc register"));
- break;
+ 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);
- case '0': /* DSP 6-bit signed immediate in bit 20. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_DSPSFT + 1) >> 1);
- max_range = ((OP_MASK_DSPSFT + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range ||
- imm_expr.X_add_number > max_range)
- {
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, DSPSFT, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ 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 '\'': /* 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;
+ break;
- case ':': /* DSP 7-bit signed immediate in bit 19. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1);
- max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range ||
- imm_expr.X_add_number > max_range)
- {
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, DSPSFT_7, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case 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;
- case '@': /* DSP 10-bit signed immediate in bit 16. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_IMM10 + 1) >> 1);
- max_range = ((OP_MASK_IMM10 + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range ||
- imm_expr.X_add_number > max_range)
- {
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, IMM10, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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;
- 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 M_LI:
+ case M_LI_S:
+ load_register (op[0], &imm_expr, 0);
+ break;
- 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 M_DLI:
+ load_register (op[0], &imm_expr, 1);
+ break;
- case '*': /* Four DSP accumulators in bits 18,19. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_T, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp/smartmips acc register"));
- break;
+ 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 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 '&': /* Four DSP accumulators in bits 13,14. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
+ if (target_big_endian)
{
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_D, *ip, regno);
- continue;
+ hreg = op[0];
+ lreg = op[0] + 1;
}
else
- as_bad (_("Invalid dsp/smartmips acc register"));
- break;
-
- case '\\': /* 3-bit bit position. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_3BITPOS
- : OP_MASK_3BITPOS);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Bit position for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- 3BITPOS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case ',':
- ++argnum;
- if (*s++ == *args)
- continue;
- s--;
- switch (*++args)
{
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
- continue;
-
- case 'w':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
- continue;
-
- case 'W':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FT, *ip, lastregno);
- continue;
-
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- continue;
+ hreg = op[0] + 1;
+ lreg = op[0];
}
- break;
-
- case '(':
- /* Handle optional base register.
- Either the base register is omitted or
- we must have a left paren. */
- /* This is dependent on the next operand specifier
- is a base register specification. */
- gas_assert (args[1] == 'b'
- || (mips_opts.micromips
- && args[1] == 'm'
- && (args[2] == 'l' || args[2] == 'n'
- || args[2] == 's' || args[2] == 'a')));
- if (*s == '\0' && args[1] == 'b')
- return;
- /* Fall through. */
-
- case ')': /* These must match exactly. */
- if (*s++ == *args)
- continue;
- break;
-
- case '[': /* These must match exactly. */
- case ']':
- gas_assert (!mips_opts.micromips);
- if (*s++ == *args)
- continue;
- break;
- case '+': /* Opcode extension character. */
- switch (*++args)
+ if (hreg <= 31)
+ load_register (hreg, &imm_expr, 0);
+ if (lreg <= 31)
{
- 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)
- internalError ();
- 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 'A': /* ins/ext position, becomes LSB. */
- limlo = 0;
- limhi = 31;
- goto do_lsb;
- case 'E':
- limlo = 32;
- limhi = 63;
- goto do_lsb;
- do_lsb:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number < limlo
- || (unsigned long) imm_expr.X_add_number > limhi)
- {
- as_bad (_("Improper position (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = limlo;
- }
- lastpos = imm_expr.X_add_number;
- INSERT_OPERAND (mips_opts.micromips,
- EXTLSB, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'B': /* ins size, becomes MSB. */
- limlo = 1;
- limhi = 32;
- goto do_msb;
- case 'F':
- limlo = 33;
- limhi = 64;
- goto do_msb;
- do_msb:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- /* Check for negative input so that small negative numbers
- will not succeed incorrectly. The checks against
- (pos+size) transitively check "size" itself,
- assuming that "pos" is reasonable. */
- if ((long) imm_expr.X_add_number < 0
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) < limlo
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) > limhi)
- {
- as_bad (_("Improper insert size (%lu, position %lu)"),
- (unsigned long) imm_expr.X_add_number,
- (unsigned long) lastpos);
- imm_expr.X_add_number = limlo - lastpos;
- }
- INSERT_OPERAND (mips_opts.micromips, INSMSB, *ip,
- lastpos + imm_expr.X_add_number - 1);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'C': /* ext size, becomes MSBD. */
- limlo = 1;
- limhi = 32;
- goto do_msbd;
- case 'G':
- limlo = 33;
- limhi = 64;
- goto do_msbd;
- case 'H':
- limlo = 33;
- limhi = 64;
- goto do_msbd;
- do_msbd:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- /* Check for negative input so that small negative numbers
- will not succeed incorrectly. The checks against
- (pos+size) transitively check "size" itself,
- assuming that "pos" is reasonable. */
- if ((long) imm_expr.X_add_number < 0
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) < limlo
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) > limhi)
- {
- as_bad (_("Improper extract size (%lu, position %lu)"),
- (unsigned long) imm_expr.X_add_number,
- (unsigned long) lastpos);
- imm_expr.X_add_number = limlo - lastpos;
- }
- INSERT_OPERAND (mips_opts.micromips,
- EXTMSBD, *ip, imm_expr.X_add_number - 1);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'D':
- /* +D is for disassembly only; never match. */
- break;
-
- case 'I':
- /* "+I" is like "I", except that imm2_expr is used. */
- my_getExpression (&imm2_expr, s);
- if (imm2_expr.X_op != O_big
- && imm2_expr.X_op != O_constant)
- insn_error = _("absolute expression required");
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (&imm2_expr);
- s = expr_end;
- continue;
-
- case 'T': /* Coprocessor register. */
- gas_assert (!mips_opts.micromips);
- /* +T is for disassembly only; never match. */
- break;
-
- 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;
- }
- }
+ if (offset_expr.X_op == O_absent)
+ move_register (lreg, 0);
else
- as_bad (_("Invalid coprocessor 0 register number"));
- 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;
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (lreg, &offset_expr, 0);
}
- INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- 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;
+ }
+ }
+ break;
+ }
+ gas_assert (imm_expr.X_op == O_absent);
- case 'p':
- /* cins, cins32, exts and exts32 position field. Give error
- if it's not in the valid range. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned) imm_expr.X_add_number > 31)
- {
- as_bad (_("Improper position (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- /* Make the pos explicit to simplify +S. */
- lastpos = imm_expr.X_add_number + 32;
- INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* 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 '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;
+ /* 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;
- case 's':
- /* cins and exts length-minus-one field. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 31)
- {
- as_bad (_("Improper size (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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 && GPR_SIZE == 64)
+ macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
+ else
+ {
+ if (ISA_HAS_MXHC1 (mips_opts.isa))
+ macro_build (NULL, "mthc1", "t,G", AT, op[0]);
+ else if (FPR_SIZE != 32)
+ as_bad (_("Unable to generate `%s' compliant code "
+ "without mthc1"),
+ (FPR_SIZE == 64) ? "fp64" : "fpxx");
+ 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
+ {
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (AT, &offset_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, op[0]);
+ }
+ }
+ break;
+ }
- case 'S':
- /* cins32/exts32 and cins/exts aliasing cint32/exts32
- length-minus-one field. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((long) imm_expr.X_add_number < 0
- || (unsigned long) imm_expr.X_add_number + lastpos > 63)
- {
- as_bad (_("Improper size (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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 '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;
+ 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 */
- 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;
+ 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 '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;
+ 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 'c': /* 9-bit signed offset in bit 6 */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
- max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
- /* We check the offset range before adjusted. */
- min_range <<= 4;
- max_range <<= 4;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- {
- as_bad (_("Offset not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- if (imm_expr.X_add_number & 0xf)
- {
- as_bad (_("Offset not 16 bytes alignment (%ld)"),
- (long) imm_expr.X_add_number);
- }
- /* Right shift 4 bits to adjust the offset operand. */
- INSERT_OPERAND (0, OFFSET_C, *ip,
- imm_expr.X_add_number >> 4);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_LQ_AB:
+ fmt = "t,o(b)";
+ s = "lq";
+ goto ld;
- case 'z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
- break;
- if (regno == AT && mips_opts.at)
- {
- if (mips_opts.at == ATREG)
- as_warn (_("used $at without \".set noat\""));
- else
- as_warn (_("used $%u with \".set at=$%u\""),
- regno, mips_opts.at);
- }
- INSERT_OPERAND (0, RZ, *ip, regno);
- continue;
+ case M_SQ_AB:
+ fmt = "t,o(b)";
+ s = "sq";
+ goto ld_st;
- case 'Z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_FPU, ®no))
- break;
- INSERT_OPERAND (0, FZ, *ip, regno);
- continue;
+ case M_LD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "ld";
+ goto ld;
+ }
+ s = "lw";
+ goto ldd_std;
- default:
- as_bad (_("Internal error: bad %s opcode "
- "(unknown extension operand type `+%c'): %s %s"),
- mips_opts.micromips ? "microMIPS" : "MIPS",
- *args, insn->name, insn->args);
- /* Further processing is fruitless. */
- return;
- }
- break;
+ case M_SD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "sd";
+ goto ld_st;
+ }
+ s = "sw";
- case '.': /* 10-bit offset. */
- gas_assert (mips_opts.micromips);
- case '~': /* 12-bit offset. */
- {
- int shift = *args == '.' ? 9 : 11;
- size_t i;
+ 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;
- /* 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;
+ 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 this value won't fit into the offset, then go find
- a macro that will generate a 16- or 32-bit offset code
- pattern. */
- i = my_getSmallExpression (&imm_expr, imm_reloc, s);
- if ((i == 0 && (imm_expr.X_op != O_constant
- || imm_expr.X_add_number >= 1 << shift
- || imm_expr.X_add_number < -1 << shift))
- || i > 0)
- {
- imm_expr.X_op = O_absent;
- break;
- }
- if (shift == 9)
- INSERT_OPERAND (1, OFFSET10, *ip, imm_expr.X_add_number);
- else
- INSERT_OPERAND (mips_opts.micromips,
- OFFSET12, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- 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;
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
- 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;
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ }
- 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 (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)
{
- 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;
+ tempreg = mips_gp_register;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ 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;
+ /* 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;
- 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;
+ /* 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;
- 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;
+ relax_switch ();
- 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);
- INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ offset_expr.X_add_number -= 4;
+ }
+ used_at = 1;
+ if (offset_high_part (offset_expr.X_add_number, 16)
+ != offset_high_part (offset_expr.X_add_number + 4, 16))
+ {
+ load_address (AT, &offset_expr, &used_at);
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ macro_build_lui (&offset_expr, AT);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? 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 '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;
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
- 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;
+ /* 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 ();
- if (c == 's' && !strncmp (ip->insn_mo->name, "jalr", 4))
- {
- if (regno == lastregno)
- {
- insn_error
- = _("Source and destination must be different");
- continue;
- }
- if (regno == 31 && lastregno == 0xffffffff)
- {
- insn_error
- = _("A destination register must be supplied");
- continue;
- }
- }
- /* Now that we have assembled one operand, we use the args
- string to figure out where it goes in the instruction. */
- switch (c)
- {
- case 'r':
- case 's':
- case 'v':
- case 'b':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, regno);
- break;
-
- case 'K':
- if (mips_opts.micromips)
- INSERT_OPERAND (1, RS, *ip, regno);
- else
- INSERT_OPERAND (0, RD, *ip, regno);
- break;
-
- case 'd':
- case 'g':
- INSERT_OPERAND (mips_opts.micromips, RD, *ip, regno);
- break;
-
- case 'U':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, RD, *ip, regno);
- INSERT_OPERAND (0, RT, *ip, regno);
- break;
-
- case 'w':
- case 't':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, regno);
- break;
-
- case 'y':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, RS3, *ip, regno);
- break;
-
- case 'x':
- /* This case exists because on the r3000 trunc
- expands into a macro which requires a gp
- register. On the r6000 or r4000 it is
- assembled into a single instruction which
- ignores the register. Thus the insn version
- is MIPS_ISA2 and uses 'x', and the macro
- version is MIPS_ISA1 and uses 't'. */
- break;
-
- case 'z':
- /* This case is for the div instruction, which
- acts differently if the destination argument
- is $0. This only matches $0, and is checked
- outside the switch. */
- break;
- }
- lastregno = regno;
- continue;
- }
- switch (*args++)
- {
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
- continue;
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_big_got)
+ {
+ int gpdelay;
- case 'w':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
- continue;
- }
- break;
+ /* 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 '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;
+ /* 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;
- case 'Q': /* MDMX vector, element sel, or const. */
- if (s[0] != '$')
- {
- /* MDMX Immediate. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
- as_warn (_("Invalid MDMX Immediate (%ld)"),
- (long) imm_expr.X_add_number);
- INSERT_OPERAND (0, FT, *ip, imm_expr.X_add_number);
- if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
- ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
- else
- ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
- }
- /* Not MDMX Immediate. Fall through. */
- case 'X': /* MDMX destination register. */
- case 'Y': /* MDMX source register. */
- case 'Z': /* MDMX target register. */
- is_mdmx = 1;
- case 'W':
- gas_assert (!mips_opts.micromips);
- case 'D': /* Floating point destination register. */
- case 'S': /* Floating point source register. */
- case 'T': /* Floating point target register. */
- case 'R': /* Floating point source register. */
- case 'V':
- rtype = RTYPE_FPU;
- if (is_mdmx
- || (mips_opts.ase_mdmx
- && (ip->insn_mo->pinfo & FP_D)
- && (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY
- | INSN_COPROC_MEMORY_DELAY
- | INSN_LOAD_COPROC_DELAY
- | INSN_LOAD_MEMORY_DELAY
- | INSN_STORE_MEMORY))))
- rtype |= RTYPE_VEC;
- s_reset = s;
- if (reg_lookup (&s, rtype, ®no))
- {
- if ((regno & 1) != 0
- && HAVE_32BIT_FPRS
- && !mips_oddfpreg_ok (ip->insn_mo, argnum))
- as_warn (_("Float register should be even, was %d"),
- regno);
-
- c = *args;
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'V' || c == 'W')
- {
- regno = lastregno;
- s = s_reset;
- ++args;
- }
- }
- switch (c)
- {
- case 'D':
- case 'X':
- INSERT_OPERAND (mips_opts.micromips, FD, *ip, regno);
- break;
-
- case 'V':
- case 'S':
- case 'Y':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, regno);
- break;
-
- case 'Q':
- /* This is like 'Z', but also needs to fix the MDMX
- vector/scalar select bits. Note that the
- scalar immediate case is handled above. */
- if (*s == '[')
- {
- int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
- int max_el = (is_qh ? 3 : 7);
- s++;
- my_getExpression(&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- s = expr_end;
- if (imm_expr.X_add_number > max_el)
- as_bad (_("Bad element selector %ld"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number &= max_el;
- ip->insn_opcode |= (imm_expr.X_add_number
- << (OP_SH_VSEL +
- (is_qh ? 2 : 1)));
- imm_expr.X_op = O_absent;
- if (*s != ']')
- as_warn (_("Expecting ']' found '%s'"), s);
- else
- s++;
- }
- else
- {
- if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
- ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
- << OP_SH_VSEL);
- else
- ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB <<
- OP_SH_VSEL);
- }
- /* Fall through. */
- case 'W':
- case 'T':
- case 'Z':
- INSERT_OPERAND (mips_opts.micromips, FT, *ip, regno);
- break;
-
- case 'R':
- INSERT_OPERAND (mips_opts.micromips, FR, *ip, regno);
- break;
- }
- lastregno = regno;
- continue;
- }
+ 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;
- switch (*args++)
- {
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- 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 'W':
- INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
- continue;
- }
- break;
+ break;
- case 'I':
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op != O_big
- && imm_expr.X_op != O_constant)
- insn_error = _("absolute expression required");
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (&imm_expr);
- s = expr_end;
- continue;
+ case M_SAA_AB:
+ s = "saa";
+ goto saa_saad;
+ case M_SAAD_AB:
+ s = "saad";
+ saa_saad:
+ gas_assert (!mips_opts.micromips);
+ offbits = 0;
+ fmt = "t,(b)";
+ goto ld_st;
- case 'A':
- my_getExpression (&offset_expr, s);
- normalize_address_expr (&offset_expr);
- *imm_reloc = BFD_RELOC_32;
- 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 '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 if (sizeof (imm_expr.X_add_number) > 4)
- {
- 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
- {
- imm_expr.X_op = O_big;
- imm_expr.X_add_number = 4;
- if (!target_big_endian)
- {
- generic_bignum[0] = bfd_getl16 (temp);
- generic_bignum[1] = bfd_getl16 (temp + 2);
- generic_bignum[2] = bfd_getl16 (temp + 4);
- generic_bignum[3] = bfd_getl16 (temp + 6);
- }
- else
- {
- generic_bignum[0] = bfd_getb16 (temp + 6);
- generic_bignum[1] = bfd_getb16 (temp + 4);
- generic_bignum[2] = bfd_getb16 (temp + 2);
- generic_bignum[3] = bfd_getb16 (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);
- if (IS_ELF)
- bfd_set_section_flags (stdoutput, new_seg,
- (SEC_ALLOC
- | SEC_LOAD
- | SEC_READONLY
- | SEC_DATA));
- frag_align (*args == 'l' ? 2 : 3, 0, 0);
- if (IS_ELF && 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;
+ FIXME: Currently, we require that the user handle delays.
+ In order to fill delay slots for non-interlocked chips,
+ we must have a way to specify delays based on the coprocessor.
+ Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+ What are the side-effects of the cop instruction?
+ What cache support might we have and what are its effects?
+ Both coprocessor & memory require delays. how long???
+ What registers are read/set/modified?
- case 'i': /* 16-bit unsigned immediate. */
- case 'j': /* 16-bit signed immediate. */
- *imm_reloc = BFD_RELOC_LO16;
- if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
- {
- int more;
- offsetT minval, maxval;
-
- more = (insn + 1 < past
- && strcmp (insn->name, insn[1].name) == 0);
-
- /* If the expression was written as an unsigned number,
- only treat it as signed if there are no more
- alternatives. */
- if (more
- && *args == 'j'
- && sizeof (imm_expr.X_add_number) <= 4
- && imm_expr.X_op == O_constant
- && imm_expr.X_add_number < 0
- && imm_expr.X_unsigned
- && HAVE_64BIT_GPRS)
- break;
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
- /* For compatibility with older assemblers, we accept
- 0x8000-0xffff as signed 16-bit numbers when only
- signed numbers are allowed. */
- if (*args == 'i')
- minval = 0, maxval = 0xffff;
- else if (more)
- minval = -0x8000, maxval = 0x7fff;
- else
- minval = -0x8000, maxval = 0xffff;
+ case M_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;
- if (imm_expr.X_op != O_constant
- || imm_expr.X_add_number < minval
- || imm_expr.X_add_number > maxval)
- {
- if (more)
- break;
- if (imm_expr.X_op == O_constant
- || imm_expr.X_op == O_big)
- as_bad (_("Expression out of range"));
- }
- }
- s = expr_end;
- continue;
+ case M_MOVE:
+ move_register (op[0], op[1]);
+ break;
- case 'o': /* 16-bit offset. */
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
+ case M_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;
- /* 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_DMUL:
+ dbl = 1;
+ /* Fall through. */
+ 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 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_DMUL_I:
+ dbl = 1;
+ /* Fall through. */
+ 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;
- s = expr_end;
- continue;
+ case M_DMULO_I:
+ dbl = 1;
+ /* Fall through. */
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
- case 'p': /* PC-relative offset. */
- *offset_reloc = BFD_RELOC_16_PCREL_S2;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ case M_DMULO:
+ dbl = 1;
+ /* Fall through. */
+ 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;
- case 'u': /* Upper 16 bits. */
- if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
- && imm_expr.X_op == O_constant
- && (imm_expr.X_add_number < 0
- || imm_expr.X_add_number >= 0x10000))
- as_bad (_("lui expression (%lu) not in range 0..65535"),
- (unsigned long) imm_expr.X_add_number);
- s = expr_end;
- continue;
+ case M_DMULOU_I:
+ dbl = 1;
+ /* Fall through. */
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
- case 'a': /* 26-bit address. */
- *offset_reloc = BFD_RELOC_MIPS_JMP;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ case M_DMULOU:
+ dbl = 1;
+ /* Fall through. */
+ 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;
- case 'N': /* 3-bit branch condition code. */
- case 'M': /* 3-bit compare condition code. */
- rtype = RTYPE_CCC;
- if (ip->insn_mo->pinfo & (FP_D | FP_S))
- rtype |= RTYPE_FCC;
- if (!reg_lookup (&s, rtype, ®no))
- break;
- if ((strcmp (str + strlen (str) - 3, ".ps") == 0
- || strcmp (str + strlen (str) - 5, "any2f") == 0
- || strcmp (str + strlen (str) - 5, "any2t") == 0)
- && (regno & 1) != 0)
- as_warn (_("Condition code register should be even for %s, "
- "was %d"),
- str, regno);
- if ((strcmp (str + strlen (str) - 5, "any4f") == 0
- || strcmp (str + strlen (str) - 5, "any4t") == 0)
- && (regno & 3) != 0)
- as_warn (_("Condition code register should be 0 or 4 for %s, "
- "was %d"),
- str, regno);
- if (*args == 'N')
- INSERT_OPERAND (mips_opts.micromips, BCC, *ip, regno);
- else
- INSERT_OPERAND (mips_opts.micromips, CCC, *ip, regno);
- continue;
+ case M_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;
- case 'H':
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- s += 2;
- if (ISDIGIT (*s))
- {
- c = 0;
- do
- {
- c *= 10;
- c += *s - '0';
- ++s;
- }
- while (ISDIGIT (*s));
- }
- else
- c = 8; /* Invalid sel value. */
+ 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;
- if (c > 7)
- as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
- INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
- continue;
+ case M_DROL_I:
+ {
+ unsigned int rot;
+ const char *l;
+ const char *rr;
- case 'e':
- gas_assert (!mips_opts.micromips);
- /* Must be at least one digit. */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ 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;
- if ((unsigned long) imm_expr.X_add_number
- > (unsigned long) OP_MASK_VECBYTE)
- {
- as_bad (_("bad byte vector index (%ld)"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
+ case M_ROL_I:
+ {
+ unsigned int rot;
- INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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;
- case '%':
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ 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;
- 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_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;
- INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_DROR_I:
+ {
+ unsigned int rot;
+ const char *l;
+ const char *rr;
- 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;
+ 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 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- case 'g':
- case 'h':
- case 'i':
- case 'j':
- case 'l':
- case 'm':
- case 'n':
- case 'p':
- case 'q':
- case 's':
- case 't':
- case 'x':
- case 'y':
- case 'z':
- s_reset = s;
- ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
- if (regno == AT && mips_opts.at)
- {
- if (mips_opts.at == ATREG)
- as_warn (_("Used $at without \".set noat\""));
- else
- as_warn (_("Used $%u with \".set at=$%u\""),
- regno, mips_opts.at);
- }
- if (!ok)
- {
- if (c == 'c')
- {
- gas_assert (args[1] == ',');
- regno = lastregno;
- ++args;
- }
- else if (c == 't')
- {
- gas_assert (args[1] == ',');
- ++args;
- continue; /* Nothing to do. */
- }
- else
- break;
- }
+ case M_ROR_I:
+ {
+ unsigned int rot;
+
+ 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;
- 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_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;
- 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_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;
- /* Make sure regno is the same as lastregno. */
- if (c == 't' && regno != lastregno)
- break;
+ 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;
- /* Make sure regno is the same as destregno. */
- if (c == 'x' && regno != destregno)
- 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;
- /* We need to save regno, before regno maps to the
- microMIPS register encoding. */
- lastregno = regno;
+ 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;
- if (c == 'f')
- destregno = regno;
+ 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;
- switch (c)
- {
- case 'a':
- if (regno != GP)
- regno = ILLEGAL_REG;
- break;
-
- case 'b':
- regno = mips32_to_micromips_reg_b_map[regno];
- break;
-
- case 'c':
- regno = mips32_to_micromips_reg_c_map[regno];
- break;
-
- case 'd':
- regno = mips32_to_micromips_reg_d_map[regno];
- break;
-
- case 'e':
- regno = mips32_to_micromips_reg_e_map[regno];
- break;
-
- case 'f':
- regno = mips32_to_micromips_reg_f_map[regno];
- break;
-
- case 'g':
- regno = mips32_to_micromips_reg_g_map[regno];
- break;
-
- case 'h':
- regno = mips32_to_micromips_reg_h_map[regno];
- break;
-
- case 'i':
- switch (EXTRACT_OPERAND (1, MI, *ip))
- {
- case 4:
- if (regno == 21)
- regno = 3;
- else if (regno == 22)
- regno = 4;
- else if (regno == 5)
- regno = 5;
- else if (regno == 6)
- regno = 6;
- else if (regno == 7)
- regno = 7;
- else
- regno = ILLEGAL_REG;
- break;
-
- case 5:
- if (regno == 6)
- regno = 0;
- else if (regno == 7)
- regno = 1;
- else
- regno = ILLEGAL_REG;
- break;
-
- case 6:
- if (regno == 7)
- regno = 2;
- else
- regno = ILLEGAL_REG;
- break;
-
- default:
- regno = ILLEGAL_REG;
- break;
- }
- break;
-
- case 'l':
- regno = mips32_to_micromips_reg_l_map[regno];
- break;
-
- case 'm':
- regno = mips32_to_micromips_reg_m_map[regno];
- break;
-
- case 'n':
- regno = mips32_to_micromips_reg_n_map[regno];
- break;
-
- case 'q':
- regno = mips32_to_micromips_reg_q_map[regno];
- break;
-
- case 's':
- if (regno != SP)
- regno = ILLEGAL_REG;
- break;
-
- case 'y':
- if (regno != 31)
- regno = ILLEGAL_REG;
- break;
-
- case 'z':
- if (regno != ZERO)
- regno = ILLEGAL_REG;
- break;
-
- case 'j': /* Do nothing. */
- case 'p':
- case 't':
- case 'x':
- break;
-
- default:
- internalError ();
- }
+ 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;
- if (regno == ILLEGAL_REG)
- break;
+ 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;
- switch (c)
- {
- case 'b':
- INSERT_OPERAND (1, MB, *ip, regno);
- break;
-
- case 'c':
- INSERT_OPERAND (1, MC, *ip, regno);
- break;
-
- case 'd':
- INSERT_OPERAND (1, MD, *ip, regno);
- break;
-
- case 'e':
- INSERT_OPERAND (1, ME, *ip, regno);
- break;
-
- case 'f':
- INSERT_OPERAND (1, MF, *ip, regno);
- break;
-
- case 'g':
- INSERT_OPERAND (1, MG, *ip, regno);
- break;
-
- case 'h':
- INSERT_OPERAND (1, MH, *ip, regno);
- break;
-
- case 'i':
- INSERT_OPERAND (1, MI, *ip, regno);
- break;
-
- case 'j':
- INSERT_OPERAND (1, MJ, *ip, regno);
- break;
-
- case 'l':
- INSERT_OPERAND (1, ML, *ip, regno);
- break;
-
- case 'm':
- INSERT_OPERAND (1, MM, *ip, regno);
- break;
-
- case 'n':
- INSERT_OPERAND (1, MN, *ip, regno);
- break;
-
- case 'p':
- INSERT_OPERAND (1, MP, *ip, regno);
- break;
-
- case 'q':
- INSERT_OPERAND (1, MQ, *ip, regno);
- break;
-
- case 'a': /* Do nothing. */
- case 's': /* Do nothing. */
- case 't': /* Do nothing. */
- case 'x': /* Do nothing. */
- case 'y': /* Do nothing. */
- case 'z': /* Do nothing. */
- break;
-
- default:
- internalError ();
- }
- 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 'A':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMA, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -64, 64, 2))
- break;
-
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMA, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_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;
- 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;
+ 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;
- 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;
+ 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 'D': /* pc relative offset */
- case 'E': /* pc relative offset */
- my_getExpression (&offset_expr, s);
- if (offset_expr.X_op == O_register)
- 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 (!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;
+ 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;
- case 'F':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_TRUNCWS:
+ case M_TRUNCWD:
+ gas_assert (!mips_opts.micromips);
+ gas_assert (mips_opts.isa == ISA_MIPS1);
+ used_at = 1;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+ /*
+ * 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;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMF, *ip, imm);
- }
- 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;
- 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;
+ uld_st:
+ breg = op[2];
+ large_offset = !small_offset_p (off, align, offbits);
+ ep = &offset_expr;
+ expr1.X_add_number = 0;
+ if (large_offset)
+ {
+ used_at = 1;
+ tempreg = AT;
+ if (small_offset_p (0, align, 16))
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ else
+ {
+ load_address (tempreg, ep, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ }
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ breg = tempreg;
+ tempreg = op[0];
+ ep = &expr1;
+ }
+ else if (!ust && op[0] == breg)
+ {
+ used_at = 1;
+ tempreg = AT;
+ }
+ else
+ tempreg = op[0];
- case 'H':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMH, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 1))
- break;
-
- imm = ep.X_add_number >> 1;
- INSERT_OPERAND (1, IMMH, *ip, imm);
- }
- s = expr_end;
- continue;
+ if (off == 1)
+ goto ulh_sh;
- case 'I':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ if (!target_big_endian)
+ ep->X_add_number += off;
+ if (offbits == 12)
+ macro_build (NULL, s, "t,~(b)", tempreg, (int) ep->X_add_number, breg);
+ else
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -1, 127, 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);
+ else
+ macro_build (ep, s2, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- imm = ep.X_add_number & 127;
- INSERT_OPERAND (1, IMMI, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* 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;
- 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;
+ 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);
- 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;
+ /* 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 'M':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ 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 (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 1, 9, 0))
- break;
+ /* For M_USH_A re-retrieve the LSB. */
+ if (ust && large_offset)
+ {
+ if (target_big_endian)
+ ep->X_add_number += off;
+ else
+ ep->X_add_number -= off;
+ macro_build (&expr1, "lbu", "t,o(b)", AT, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], AT);
+ }
+ /* For ULH and M_USH_A OR the LSB in. */
+ if (!ust || large_offset)
+ {
+ tempreg = !large_offset ? AT : op[0];
+ macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
- imm = ep.X_add_number & 7;
- INSERT_OPERAND (1, IMMM, *ip, imm);
- }
- s = expr_end;
- continue;
+ 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\""));
+}
- 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.:
+/* Implement macros in mips16 mode. */
- s0, ra
- s0, s1, ra, s2, s3
- s0-s2, ra
+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;
- and any permutations of these. */
- unsigned int reglist;
- int imm;
+ mask = ip->insn_mo->mask;
- if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
- break;
+ 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;
- if ((reglist & 0xfff1ffff) != 0x80010000)
- break;
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
- reglist = (reglist >> 17) & 7;
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+ dbl = 0;
- imm = ffs (reglist) - 1;
- INSERT_OPERAND (1, IMMN, *ip, imm);
- }
- continue;
+ switch (mask)
+ {
+ default:
+ abort ();
- case 'O': /* sdbbp 4-bit code. */
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_DDIV_3:
+ dbl = 1;
+ /* Fall through. */
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ /* Fall through. */
+ 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);
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+ /* 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;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMO, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ start_noreorder ();
+ macro_build (NULL, s, "0,x,y", 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;
- case 'P':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_DMUL:
+ dbl = 1;
+ /* Fall through. */
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
+ macro_build (NULL, "mflo", "x", op[0]);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 32, 2))
- 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;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMP, *ip, imm);
- }
- s = expr_end;
- continue;
+ 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 'Q':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ 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 (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
- break;
+ 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";
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMQ, *ip, imm);
- }
- s = expr_end;
- continue;
+ do_reverse_branch:
+ tmp = op[1];
+ op[1] = op[0];
+ op[0] = tmp;
- 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;
+ do_branch:
+ macro_build (NULL, s, "x,y", op[0], op[1]);
+ macro_build (&offset_expr, s2, "p");
+ break;
- case 'W':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ 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 (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 64, 2))
- break;
+ do_addone_branch_i:
+ ++imm_expr.X_add_number;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMW, *ip, imm);
- }
- s = expr_end;
- continue;
+ do_branch_i:
+ macro_build (&imm_expr, s, s3, op[0]);
+ macro_build (&offset_expr, s2, "p");
+ break;
- case 'X':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "slti", "x,8", 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;
+ }
+}
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -8, 8, 0))
- break;
+/* Look up instruction [START, START + LENGTH) in HASH. Record any extra
+ opcode bits in *OPCODE_EXTRA. */
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMX, *ip, imm);
- }
- s = expr_end;
- continue;
+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;
- 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;
+ /* Make a copy of the instruction so that we can fiddle with it. */
+ name = xstrndup (start, length);
- case 'Z':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
+ /* Look up the instruction as-is. */
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ if (insn)
+ goto end;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 1, 0))
- break;
- }
- s = expr_end;
- continue;
+ 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;
+ goto end;
+ }
+ }
+ }
- 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;
+ 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;
+ goto end;
+ }
+ }
+ }
- 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;
+ insn = NULL;
+ end:
+ free (name);
+ return insn;
+}
- if ((reglist & 0x3f00ffff) != 0)
- break;
+/* 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. */
- ra = (reglist >> 27) & 0x10;
- reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+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;
- imm = (ffs (reglist) - 1) | ra;
- INSERT_OPERAND (1, RT, *ip, imm);
- imm_expr.X_op = O_absent;
- }
- continue;
+ if (mips_opts.micromips)
+ {
+ hash = micromips_op_hash;
+ past = µmips_opcodes[bfd_micromips_num_opcodes];
+ }
+ else
+ {
+ hash = op_hash;
+ past = &mips_opcodes[NUMOPCODES];
+ }
+ forced_insn_length = 0;
+ opcode_extra = 0;
- 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;
+ /* 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;
- default:
- as_bad (_("Bad char = '%c'\n"), *args);
- internalError ();
- }
- break;
- }
- /* Args don't match. */
- s = argsStart;
- insn_error = _("Illegal operands");
- if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
- {
- ++insn;
- continue;
- }
- else if (wrong_delay_slot_insns && need_delay_slot_ok)
- {
- gas_assert (firstinsn);
- need_delay_slot_ok = FALSE;
- past = insn + 1;
- insn = firstinsn;
- continue;
- }
+ first = mips_lookup_insn (hash, str, end, &opcode_extra);
+ if (first == NULL)
+ {
+ set_insn_error (0, _("unrecognized opcode"));
return;
}
-}
-#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
+ 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;
-/* This routine assembles an instruction into its binary format when
- assembling for the mips16. As a side effect, it sets one of the
- global variables imm_reloc or offset_reloc to the type of relocation
- to do if one of the operands is an address expression. It also sets
- forced_insn_length to the resulting instruction size in bytes if the
- user explicitly requested a small or extended instruction. */
+ 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"));
-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;
+ obstack_free (&mips_operand_tokens, tokens);
+}
- insn_error = NULL;
+/* 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. */
- forced_insn_length = 0;
+static void
+mips16_ip (char *str, struct mips_cl_insn *insn)
+{
+ char *end, *s, c;
+ struct mips_opcode *first;
+ struct mips_operand_token *tokens;
+ unsigned int l;
for (s = str; ISLOWER (*s); ++s)
;
- switch (*s)
+ end = s;
+ c = *end;
+
+ l = 0;
+ switch (c)
{
case '\0':
break;
case ' ':
- *s++ = '\0';
+ s++;
break;
case '.':
- if (s[1] == 't' && s[2] == ' ')
+ s++;
+ if (*s == 't')
{
- *s = '\0';
- forced_insn_length = 2;
- s += 3;
- break;
+ l = 2;
+ s++;
}
- else if (s[1] == 'e' && s[2] == ' ')
+ else if (*s == 'e')
{
- *s = '\0';
- forced_insn_length = 4;
- s += 3;
- break;
+ l = 4;
+ s++;
}
+ if (*s == '\0')
+ break;
+ else if (*s++ == ' ')
+ break;
/* Fall through. */
default:
- insn_error = _("unknown opcode");
+ set_insn_error (0, _("unrecognized opcode"));
return;
}
+ forced_insn_length = l;
- if (mips_opts.noautoextend && !forced_insn_length)
- forced_insn_length = 2;
+ *end = 0;
+ first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
+ *end = c;
- if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
+ if (!first)
{
- insn_error = _("unrecognized opcode");
+ set_insn_error (0, _("unrecognized opcode"));
return;
}
- argsstart = s;
- for (;;)
- {
- bfd_boolean ok;
-
- gas_assert (strcmp (insn->name, str) == 0);
-
- 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;
- }
- }
-
- create_insn (ip, insn);
- imm_expr.X_op = O_absent;
- imm_reloc[0] = BFD_RELOC_UNUSED;
- imm_reloc[1] = BFD_RELOC_UNUSED;
- imm_reloc[2] = BFD_RELOC_UNUSED;
- imm2_expr.X_op = O_absent;
- offset_expr.X_op = O_absent;
- offset_reloc[0] = BFD_RELOC_UNUSED;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
- for (args = insn->args; 1; ++args)
- {
- int c;
-
- if (*s == ' ')
- ++s;
-
- /* In this switch statement we call break if we did not find
- a match, continue if we did find a match, or return if we
- are done. */
-
- c = *args;
- switch (c)
- {
- case '\0':
- if (*s == '\0')
- {
- /* Stuff the immediate value in now, if we can. */
- if (imm_expr.X_op == O_constant
- && *imm_reloc > BFD_RELOC_UNUSED
- && *imm_reloc != BFD_RELOC_MIPS16_GOT16
- && *imm_reloc != BFD_RELOC_MIPS16_CALL16
- && insn->pinfo != INSN_MACRO)
- {
- valueT tmp;
-
- switch (*offset_reloc)
- {
- case BFD_RELOC_MIPS16_HI16_S:
- tmp = (imm_expr.X_add_number + 0x8000) >> 16;
- break;
-
- case BFD_RELOC_MIPS16_HI16:
- tmp = imm_expr.X_add_number >> 16;
- break;
-
- case BFD_RELOC_MIPS16_LO16:
- tmp = ((imm_expr.X_add_number + 0x8000) & 0xffff)
- - 0x8000;
- break;
-
- case BFD_RELOC_UNUSED:
- tmp = imm_expr.X_add_number;
- break;
-
- default:
- internalError ();
- }
- *offset_reloc = BFD_RELOC_UNUSED;
-
- mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
- tmp, TRUE, forced_insn_length == 2,
- forced_insn_length == 4, &ip->insn_opcode,
- &ip->use_extend, &ip->extend);
- imm_expr.X_op = O_absent;
- *imm_reloc = BFD_RELOC_UNUSED;
- }
-
- return;
- }
- break;
-
- case ',':
- if (*s++ == c)
- continue;
- s--;
- switch (*++args)
- {
- case 'v':
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- continue;
- case 'w':
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- continue;
- }
- break;
-
- case '(':
- case ')':
- if (*s++ == c)
- continue;
- 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;
- }
-
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'v' || c == 'w')
- {
- regno = mips16_to_32_reg_map[lastregno];
- s = s_reset;
- ++args;
- }
- }
-
- switch (c)
- {
- case 'x':
- case 'y':
- case 'z':
- case 'v':
- case 'w':
- case 'Z':
- regno = mips32_to_16_reg_map[regno];
- break;
-
- case '0':
- if (regno != 0)
- regno = ILLEGAL_REG;
- break;
-
- case 'S':
- if (regno != SP)
- regno = ILLEGAL_REG;
- break;
-
- case 'R':
- if (regno != RA)
- regno = ILLEGAL_REG;
- break;
-
- case 'X':
- case 'Y':
- if (regno == AT && mips_opts.at)
- {
- if (mips_opts.at == ATREG)
- as_warn (_("used $at without \".set noat\""));
- else
- as_warn (_("used $%u with \".set at=$%u\""),
- regno, mips_opts.at);
- }
- break;
-
- default:
- internalError ();
- }
-
- if (regno == ILLEGAL_REG)
- break;
-
- switch (c)
- {
- case 'x':
- case 'v':
- MIPS16_INSERT_OPERAND (RX, *ip, regno);
- break;
- case 'y':
- case 'w':
- MIPS16_INSERT_OPERAND (RY, *ip, regno);
- break;
- case 'z':
- MIPS16_INSERT_OPERAND (RZ, *ip, regno);
- break;
- case 'Z':
- MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno);
- case '0':
- case 'S':
- case 'R':
- break;
- case 'X':
- MIPS16_INSERT_OPERAND (REGR32, *ip, regno);
- break;
- case 'Y':
- regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
- MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
- break;
- default:
- internalError ();
- }
-
- lastregno = regno;
- continue;
-
- case 'P':
- if (strncmp (s, "$pc", 3) == 0)
- {
- s += 3;
- continue;
- }
- break;
-
- case '5':
- case 'H':
- case 'W':
- case 'D':
- case 'j':
- case 'V':
- case 'C':
- case 'U':
- case 'k':
- case 'K':
- i = my_getSmallExpression (&imm_expr, imm_reloc, s);
- if (i > 0)
- {
- if (imm_expr.X_op != O_constant)
- {
- forced_insn_length = 4;
- ip->use_extend = TRUE;
- ip->extend = 0;
- }
- else
- {
- /* We need to relax this instruction. */
- *offset_reloc = *imm_reloc;
- *imm_reloc = (int) BFD_RELOC_UNUSED + c;
- }
- s = expr_end;
- continue;
- }
- *imm_reloc = BFD_RELOC_UNUSED;
- /* Fall through. */
- case '<':
- case '>':
- case '[':
- case ']':
- case '4':
- case '8':
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op == O_register)
- {
- /* What we thought was an expression turned out to
- be a register. */
-
- if (s[0] == '(' && args[1] == '(')
- {
- /* It looks like the expression was omitted
- before a register indirection, which means
- that the expression is implicitly zero. We
- still set up imm_expr, so that we handle
- explicit extensions correctly. */
- imm_expr.X_op = O_constant;
- imm_expr.X_add_number = 0;
- *imm_reloc = (int) BFD_RELOC_UNUSED + c;
- continue;
- }
-
- break;
- }
-
- /* We need to relax this instruction. */
- *imm_reloc = (int) BFD_RELOC_UNUSED + c;
- s = expr_end;
- continue;
-
- case 'p':
- case 'q':
- case 'A':
- case 'B':
- case 'E':
- /* We use offset_reloc rather than imm_reloc for the PC
- relative operands. This lets macros with both
- immediate and address operands work correctly. */
- my_getExpression (&offset_expr, s);
-
- if (offset_expr.X_op == O_register)
- break;
-
- /* We need to relax this instruction. */
- *offset_reloc = (int) BFD_RELOC_UNUSED + c;
- s = expr_end;
- continue;
-
- 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;
-
- case 'a': /* 26 bit address */
- my_getExpression (&offset_expr, s);
- s = expr_end;
- *offset_reloc = BFD_RELOC_MIPS16_JMP;
- ip->insn_opcode <<= 16;
- continue;
-
- case 'l': /* register list for entry macro */
- case 'L': /* register list for exit macro */
- {
- int mask;
-
- if (c == 'l')
- mask = 0;
- else
- mask = 7 << 3;
- while (*s != '\0')
- {
- unsigned int freg, reg1, reg2;
-
- while (*s == ' ' || *s == ',')
- ++s;
- if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
- freg = 0;
- else if (reg_lookup (&s, RTYPE_FPU, ®1))
- freg = 1;
- else
- {
- as_bad (_("can't parse register list"));
- break;
- }
- if (*s == ' ')
- ++s;
- if (*s != '-')
- reg2 = reg1;
- else
- {
- ++s;
- if (!reg_lookup (&s, freg ? RTYPE_FPU
- : (RTYPE_GP | RTYPE_NUM), ®2))
- {
- as_bad (_("invalid register list"));
- break;
- }
- }
- if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
- {
- mask &= ~ (7 << 3);
- mask |= 5 << 3;
- }
- else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
- {
- mask &= ~ (7 << 3);
- mask |= 6 << 3;
- }
- else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
- mask |= (reg2 - 3) << 3;
- else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
- mask |= (reg2 - 15) << 1;
- else if (reg1 == RA && reg2 == RA)
- mask |= 1;
- else
- {
- as_bad (_("invalid register list"));
- break;
- }
- }
- /* The mask is filled in in the opcode table for the
- benefit of the disassembler. We remove it before
- applying the actual mask. */
- ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
- ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
- }
- continue;
-
- case 'm': /* Register list for save insn. */
- case 'M': /* Register list for restore insn. */
- {
- int opcode = 0;
- 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)
- {
- ip->use_extend = TRUE;
- ip->extend = opcode >> 16;
- }
- ip->insn_opcode |= opcode & 0x7f;
- }
- continue;
-
- 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;
+ tokens = mips_parse_arguments (s, 0);
+ if (!tokens)
+ return;
- default:
- internalError ();
- }
- break;
- }
+ if (!match_mips16_insns (insn, first, tokens))
+ set_insn_error (0, _("invalid operands"));
- /* Args don't match. */
- if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
- strcmp (insn->name, insn[1].name) == 0)
- {
- ++insn;
- s = argsstart;
- continue;
- }
+ obstack_free (&mips_operand_tokens, tokens);
+}
- insn_error = _("illegal operands");
+/* Marshal immediate value VAL for an extended MIPS16 instruction.
+ NBITS is the number of significant bits in VAL. */
- return;
+static unsigned long
+mips16_immed_extend (offsetT val, unsigned int nbits)
+{
+ int extval;
+ if (nbits == 16)
+ {
+ extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+ val &= 0x1f;
+ }
+ else if (nbits == 15)
+ {
+ extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+ val &= 0xf;
+ }
+ else
+ {
+ extval = ((val & 0x1f) << 6) | (val & 0x20);
+ val = 0;
}
+ return (extval << 16) | val;
}
-/* This structure holds information we know about a mips16 immediate
- argument type. */
+/* Like decode_mips16_operand, but require the operand to be defined and
+ require it to be an integer. */
-struct mips16_immed_operand
+static const struct mips_int_operand *
+mips16_immed_operand (int type, bfd_boolean extended_p)
{
- /* 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;
-};
+ const struct mips_operand *operand;
-/* 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 }
-};
-
-#define MIPS16_NUM_IMMED \
- (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+ 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;
+}
-/* Handle a mips16 instruction with an immediate value. This or's the
- small immediate value into *INSN. It sets *USE_EXTEND to indicate
- whether an extended value is needed; if one is needed, it sets
- *EXTEND to the value. The argument type is TYPE. The value is VAL.
- If SMALL is true, an unextended opcode was explicitly requested.
- If EXT is true, an extended opcode was explicitly requested. If
- WARN is true, warn if EXT does not match reality. */
+/* Return true if SVAL fits OPERAND. RELOC is as for mips16_immed. */
-static void
-mips16_immed (char *file, unsigned int line, int type, offsetT val,
- bfd_boolean warn, bfd_boolean small, bfd_boolean ext,
- unsigned long *insn, bfd_boolean *use_extend,
- unsigned short *extend)
+static bfd_boolean
+mips16_immed_in_range_p (const struct mips_int_operand *operand,
+ bfd_reloc_code_real_type reloc, offsetT sval)
{
- const struct mips16_immed_operand *op;
- int mintiny, maxtiny;
- bfd_boolean needext;
-
- op = mips16_immed_operands;
- while (op->type != type)
- {
- ++op;
- gas_assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
- }
+ int min_val, max_val;
- if (op->unsp)
+ min_val = mips_int_operand_min (operand);
+ max_val = mips_int_operand_max (operand);
+ if (reloc != BFD_RELOC_UNUSED)
{
- if (type == '<' || type == '>' || type == '[' || type == ']')
- {
- mintiny = 1;
- maxtiny = 1 << op->nbits;
- }
+ if (min_val < 0)
+ sval = SEXT_16BIT (sval);
else
- {
- mintiny = 0;
- maxtiny = (1 << op->nbits) - 1;
- }
- }
- else
- {
- mintiny = - (1 << (op->nbits - 1));
- maxtiny = (1 << (op->nbits - 1)) - 1;
+ sval &= 0xffff;
}
- /* Branch offsets have an implicit 0 in the lowest bit. */
- if (type == 'p' || type == 'q')
- val /= 2;
+ return (sval >= min_val
+ && sval <= max_val
+ && (sval & ((1 << operand->shift) - 1)) == 0);
+}
- if ((val & ((1 << op->shift) - 1)) != 0
- || val < (mintiny << op->shift)
- || val > (maxtiny << op->shift))
- needext = TRUE;
- else
- needext = FALSE;
+/* Install immediate value VAL into MIPS16 instruction *INSN,
+ extending it if necessary. The instruction in *INSN may
+ already be extended.
- if (warn && ext && ! needext)
- as_warn_where (file, line,
- _("extended operand requested but not required"));
- if (small && needext)
- as_bad_where (file, line, _("invalid unextended operand value"));
+ RELOC is the relocation that produced VAL, or BFD_RELOC_UNUSED
+ if none. In the former case, VAL is a 16-bit number with no
+ defined signedness.
- if (small || (! ext && ! needext))
- {
- int insnval;
+ TYPE is the type of the immediate field. USER_INSN_LENGTH
+ is the length that the user requested, or 0 if none. */
+
+static void
+mips16_immed (const char *file, unsigned int line, int type,
+ bfd_reloc_code_real_type reloc, offsetT val,
+ unsigned int user_insn_length, unsigned long *insn)
+{
+ const struct mips_int_operand *operand;
+ unsigned int uval, length;
- *use_extend = FALSE;
- insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
- insnval <<= op->op_shift;
- *insn |= insnval;
+ 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)
+ as_bad_where (file, line, _("invalid unextended operand value"));
+ else
+ *insn |= MIPS16_EXTEND;
}
- else
+ else if (user_insn_length == 4)
{
- long minext, maxext;
- int extval;
+ /* The operand doesn't force an unextended instruction to be extended.
+ Warn if the user wanted an extended instruction anyway. */
+ *insn |= MIPS16_EXTEND;
+ as_warn_where (file, line,
+ _("extended operand requested but not required"));
+ }
- if (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)
+ length = mips16_opcode_length (*insn);
+ if (length == 4)
+ {
+ 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"));
-
- *use_extend = TRUE;
- if (op->extbits == 16)
- {
- extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
- val &= 0x1f;
- }
- else if (op->extbits == 15)
- {
- extval = ((val >> 11) & 0xf) | (val & 0x7f0);
- val &= 0xf;
- }
- else
- {
- extval = ((val & 0x1f) << 6) | (val & 0x20);
- val = 0;
- }
-
- *extend = (unsigned short) extval;
- *insn |= val;
}
+ uval = ((unsigned int) val >> operand->shift) - operand->bias;
+ if (length == 2)
+ *insn = mips_insert_operand (&operand->root, *insn, uval);
+ else
+ *insn |= mips16_immed_extend (uval, operand->root.size);
}
\f
struct percent_op_match
static const struct percent_op_match mips_percent_op[] =
{
{"%lo", BFD_RELOC_LO16},
-#ifdef OBJ_ELF
{"%call_hi", BFD_RELOC_MIPS_CALL_HI16},
{"%call_lo", BFD_RELOC_MIPS_CALL_LO16},
{"%call16", BFD_RELOC_MIPS_CALL16},
{"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
{"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
{"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
-#endif
- {"%hi", BFD_RELOC_HI16_S}
+ {"%hi", BFD_RELOC_HI16_S},
+ {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
+ {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
};
static const struct percent_op_match mips16_percent_op[] =
{"%gprel", BFD_RELOC_MIPS16_GPREL},
{"%got", BFD_RELOC_MIPS16_GOT16},
{"%call16", BFD_RELOC_MIPS16_CALL16},
- {"%hi", BFD_RELOC_MIPS16_HI16_S}
+ {"%hi", BFD_RELOC_MIPS16_HI16_S},
+ {"%tlsgd", BFD_RELOC_MIPS16_TLS_GD},
+ {"%tlsldm", BFD_RELOC_MIPS16_TLS_LDM},
+ {"%dtprel_hi", BFD_RELOC_MIPS16_TLS_DTPREL_HI16},
+ {"%dtprel_lo", BFD_RELOC_MIPS16_TLS_DTPREL_LO16},
+ {"%tprel_hi", BFD_RELOC_MIPS16_TLS_TPREL_HI16},
+ {"%tprel_lo", BFD_RELOC_MIPS16_TLS_TPREL_LO16},
+ {"%gottprel", BFD_RELOC_MIPS16_TLS_GOTTPREL}
};
input_line_pointer = save_in;
}
-char *
+const char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
number_to_chars_littleendian (buf, val, n);
}
\f
-#ifdef OBJ_ELF
static int support_64bit_objects(void)
{
const char **list, **l;
free (list);
return yes;
}
-#endif /* OBJ_ELF */
-
-const char *md_shortopts = "O::g::G:";
-
-enum options
- {
- OPTION_MARCH = OPTION_MD_BASE,
- OPTION_MTUNE,
- OPTION_MIPS1,
- OPTION_MIPS2,
- OPTION_MIPS3,
- OPTION_MIPS4,
- OPTION_MIPS5,
- OPTION_MIPS32,
- OPTION_MIPS64,
- OPTION_MIPS32R2,
- OPTION_MIPS64R2,
- OPTION_MIPS16,
- OPTION_NO_MIPS16,
- OPTION_MIPS3D,
- OPTION_NO_MIPS3D,
- OPTION_MDMX,
- OPTION_NO_MDMX,
- OPTION_DSP,
- OPTION_NO_DSP,
- OPTION_MT,
- OPTION_NO_MT,
- OPTION_SMARTMIPS,
- OPTION_NO_SMARTMIPS,
- OPTION_DSPR2,
- OPTION_NO_DSPR2,
- OPTION_MICROMIPS,
- OPTION_NO_MICROMIPS,
- OPTION_MCU,
- OPTION_NO_MCU,
- OPTION_COMPAT_ARCH_BASE,
- OPTION_M4650,
- OPTION_NO_M4650,
- OPTION_M4010,
- OPTION_NO_M4010,
- OPTION_M4100,
- OPTION_NO_M4100,
- OPTION_M3900,
- OPTION_NO_M3900,
- OPTION_M7000_HILO_FIX,
- OPTION_MNO_7000_HILO_FIX,
- OPTION_FIX_24K,
- OPTION_NO_FIX_24K,
- OPTION_FIX_LOONGSON2F_JUMP,
- OPTION_NO_FIX_LOONGSON2F_JUMP,
- OPTION_FIX_LOONGSON2F_NOP,
- OPTION_NO_FIX_LOONGSON2F_NOP,
- OPTION_FIX_VR4120,
- OPTION_NO_FIX_VR4120,
- OPTION_FIX_VR4130,
- OPTION_NO_FIX_VR4130,
- OPTION_FIX_CN63XXP1,
- OPTION_NO_FIX_CN63XXP1,
- OPTION_TRAP,
- OPTION_BREAK,
- OPTION_EB,
- OPTION_EL,
- OPTION_FP32,
- OPTION_GP32,
- OPTION_CONSTRUCT_FLOATS,
- OPTION_NO_CONSTRUCT_FLOATS,
- OPTION_FP64,
- OPTION_GP64,
- OPTION_RELAX_BRANCH,
- OPTION_NO_RELAX_BRANCH,
- OPTION_MSHARED,
- OPTION_MNO_SHARED,
- OPTION_MSYM32,
- OPTION_MNO_SYM32,
- OPTION_SOFT_FLOAT,
- OPTION_HARD_FLOAT,
- OPTION_SINGLE_FLOAT,
- OPTION_DOUBLE_FLOAT,
- OPTION_32,
-#ifdef OBJ_ELF
- OPTION_CALL_SHARED,
- OPTION_CALL_NONPIC,
- OPTION_NON_SHARED,
- OPTION_XGOT,
- OPTION_MABI,
- OPTION_N32,
- OPTION_64,
- OPTION_MDEBUG,
- OPTION_NO_MDEBUG,
- OPTION_PDR,
- OPTION_NO_PDR,
- OPTION_MVXWORKS_PIC,
-#endif /* OBJ_ELF */
- OPTION_END_OF_ENUM
- };
-
-struct option md_longopts[] =
-{
- /* Options which specify architecture. */
- {"march", required_argument, NULL, OPTION_MARCH},
- {"mtune", required_argument, NULL, OPTION_MTUNE},
- {"mips0", no_argument, NULL, OPTION_MIPS1},
- {"mips1", no_argument, NULL, OPTION_MIPS1},
- {"mips2", no_argument, NULL, OPTION_MIPS2},
- {"mips3", no_argument, NULL, OPTION_MIPS3},
- {"mips4", no_argument, NULL, OPTION_MIPS4},
- {"mips5", no_argument, NULL, OPTION_MIPS5},
- {"mips32", no_argument, NULL, OPTION_MIPS32},
- {"mips64", no_argument, NULL, OPTION_MIPS64},
- {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
- {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
-
- /* Options which specify Application Specific Extensions (ASEs). */
- {"mips16", no_argument, NULL, OPTION_MIPS16},
- {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
- {"mips3d", no_argument, NULL, OPTION_MIPS3D},
- {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
- {"mdmx", no_argument, NULL, OPTION_MDMX},
- {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX},
- {"mdsp", no_argument, NULL, OPTION_DSP},
- {"mno-dsp", no_argument, NULL, OPTION_NO_DSP},
- {"mmt", no_argument, NULL, OPTION_MT},
- {"mno-mt", no_argument, NULL, OPTION_NO_MT},
- {"msmartmips", no_argument, NULL, OPTION_SMARTMIPS},
- {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
- {"mdspr2", no_argument, NULL, OPTION_DSPR2},
- {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
- {"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
- {"mno-micromips", no_argument, NULL, OPTION_NO_MICROMIPS},
- {"mmcu", no_argument, NULL, OPTION_MCU},
- {"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
-
- /* Old-style architecture options. Don't add more of these. */
- {"m4650", no_argument, NULL, OPTION_M4650},
- {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
- {"m4010", no_argument, NULL, OPTION_M4010},
- {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
- {"m4100", no_argument, NULL, OPTION_M4100},
- {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
- {"m3900", no_argument, NULL, OPTION_M3900},
- {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
-
- /* Options which enable bug fixes. */
- {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
- {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
- {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
- {"mfix-loongson2f-jump", no_argument, NULL, OPTION_FIX_LOONGSON2F_JUMP},
- {"mno-fix-loongson2f-jump", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_JUMP},
- {"mfix-loongson2f-nop", no_argument, NULL, OPTION_FIX_LOONGSON2F_NOP},
- {"mno-fix-loongson2f-nop", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_NOP},
- {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120},
- {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
- {"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130},
- {"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-cn63xxp1", no_argument, NULL, OPTION_FIX_CN63XXP1},
- {"mno-fix-cn63xxp1", no_argument, NULL, OPTION_NO_FIX_CN63XXP1},
-
- /* Miscellaneous options. */
- {"trap", no_argument, NULL, OPTION_TRAP},
- {"no-break", no_argument, NULL, OPTION_TRAP},
- {"break", no_argument, NULL, OPTION_BREAK},
- {"no-trap", no_argument, NULL, OPTION_BREAK},
- {"EB", no_argument, NULL, OPTION_EB},
- {"EL", no_argument, NULL, OPTION_EL},
- {"mfp32", no_argument, NULL, OPTION_FP32},
- {"mgp32", no_argument, NULL, OPTION_GP32},
- {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
- {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
- {"mfp64", no_argument, NULL, OPTION_FP64},
- {"mgp64", no_argument, NULL, OPTION_GP64},
- {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
- {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
- {"mshared", no_argument, NULL, OPTION_MSHARED},
- {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED},
- {"msym32", no_argument, NULL, OPTION_MSYM32},
- {"mno-sym32", no_argument, NULL, OPTION_MNO_SYM32},
- {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT},
- {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT},
- {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT},
- {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT},
-
- /* Strictly speaking this next option is ELF specific,
- but we allow it for other ports as well in order to
- make testing easier. */
- {"32", no_argument, NULL, OPTION_32},
-
- /* ELF-specific options. */
-#ifdef OBJ_ELF
- {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
- {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
- {"call_nonpic", no_argument, NULL, OPTION_CALL_NONPIC},
- {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
- {"xgot", no_argument, NULL, OPTION_XGOT},
- {"mabi", required_argument, NULL, OPTION_MABI},
- {"n32", no_argument, NULL, OPTION_N32},
- {"64", no_argument, NULL, OPTION_64},
- {"mdebug", no_argument, NULL, OPTION_MDEBUG},
- {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
- {"mpdr", no_argument, NULL, OPTION_PDR},
- {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
- {"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC},
-#endif /* OBJ_ELF */
-
- {NULL, no_argument, NULL, 0}
-};
-size_t md_longopts_size = sizeof (md_longopts);
/* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to
NEW_VALUE. Warn if another value was already specified. Note:
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);
}
int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if (c == mips_ases[i].option_on || c == mips_ases[i].option_off)
+ {
+ file_ase_explicit |= mips_set_ase (&mips_ases[i], &file_mips_opts,
+ c == mips_ases[i].option_on);
+ return 1;
+ }
+
switch (c)
{
case OPTION_CONSTRUCT_FLOATS:
mips_debug = atoi (arg);
break;
- case OPTION_MIPS1:
- file_mips_isa = ISA_MIPS1;
+ case OPTION_MIPS1:
+ file_mips_opts.isa = ISA_MIPS1;
+ break;
+
+ case OPTION_MIPS2:
+ file_mips_opts.isa = ISA_MIPS2;
+ break;
+
+ case OPTION_MIPS3:
+ file_mips_opts.isa = ISA_MIPS3;
+ break;
+
+ case OPTION_MIPS4:
+ file_mips_opts.isa = ISA_MIPS4;
+ break;
+
+ case OPTION_MIPS5:
+ file_mips_opts.isa = ISA_MIPS5;
+ break;
+
+ case OPTION_MIPS32:
+ file_mips_opts.isa = ISA_MIPS32;
+ break;
+
+ case OPTION_MIPS32R2:
+ file_mips_opts.isa = ISA_MIPS32R2;
break;
- case OPTION_MIPS2:
- file_mips_isa = ISA_MIPS2;
+ case OPTION_MIPS32R3:
+ file_mips_opts.isa = ISA_MIPS32R3;
break;
- case OPTION_MIPS3:
- file_mips_isa = ISA_MIPS3;
+ case OPTION_MIPS32R5:
+ file_mips_opts.isa = ISA_MIPS32R5;
break;
- case OPTION_MIPS4:
- file_mips_isa = ISA_MIPS4;
+ case OPTION_MIPS32R6:
+ file_mips_opts.isa = ISA_MIPS32R6;
break;
- case OPTION_MIPS5:
- file_mips_isa = ISA_MIPS5;
+ case OPTION_MIPS64R2:
+ file_mips_opts.isa = ISA_MIPS64R2;
break;
- case OPTION_MIPS32:
- file_mips_isa = ISA_MIPS32;
+ case OPTION_MIPS64R3:
+ file_mips_opts.isa = ISA_MIPS64R3;
break;
- case OPTION_MIPS32R2:
- file_mips_isa = ISA_MIPS32R2;
+ case OPTION_MIPS64R5:
+ file_mips_opts.isa = ISA_MIPS64R5;
break;
- case OPTION_MIPS64R2:
- file_mips_isa = ISA_MIPS64R2;
+ case OPTION_MIPS64R6:
+ file_mips_opts.isa = ISA_MIPS64R6;
break;
case OPTION_MIPS64:
- file_mips_isa = ISA_MIPS64;
+ file_mips_opts.isa = ISA_MIPS64;
break;
case OPTION_MTUNE:
case OPTION_NO_M3900:
break;
- case OPTION_MDMX:
- mips_opts.ase_mdmx = 1;
- break;
-
- case OPTION_NO_MDMX:
- mips_opts.ase_mdmx = 0;
- break;
-
- case OPTION_DSP:
- mips_opts.ase_dsp = 1;
- mips_opts.ase_dspr2 = 0;
- break;
-
- case OPTION_NO_DSP:
- mips_opts.ase_dsp = 0;
- mips_opts.ase_dspr2 = 0;
- break;
-
- case OPTION_DSPR2:
- mips_opts.ase_dspr2 = 1;
- mips_opts.ase_dsp = 1;
- break;
-
- case OPTION_NO_DSPR2:
- mips_opts.ase_dspr2 = 0;
- mips_opts.ase_dsp = 0;
- break;
-
- case OPTION_MT:
- mips_opts.ase_mt = 1;
- break;
-
- case OPTION_NO_MT:
- mips_opts.ase_mt = 0;
- break;
-
- case OPTION_MCU:
- mips_opts.ase_mcu = 1;
- break;
-
- case OPTION_NO_MCU:
- mips_opts.ase_mcu = 0;
- break;
-
case OPTION_MICROMIPS:
- if (mips_opts.mips16 == 1)
+ if (file_mips_opts.mips16 == 1)
{
as_bad (_("-mmicromips cannot be used with -mips16"));
return 0;
}
- mips_opts.micromips = 1;
+ file_mips_opts.micromips = 1;
mips_no_prev_insn ();
break;
case OPTION_NO_MICROMIPS:
- mips_opts.micromips = 0;
+ file_mips_opts.micromips = 0;
mips_no_prev_insn ();
break;
case OPTION_MIPS16:
- if (mips_opts.micromips == 1)
+ if (file_mips_opts.micromips == 1)
{
as_bad (_("-mips16 cannot be used with -micromips"));
return 0;
}
- mips_opts.mips16 = 1;
+ file_mips_opts.mips16 = 1;
mips_no_prev_insn ();
break;
case OPTION_NO_MIPS16:
- mips_opts.mips16 = 0;
+ file_mips_opts.mips16 = 0;
mips_no_prev_insn ();
break;
- case OPTION_MIPS3D:
- mips_opts.ase_mips3d = 1;
- break;
-
- case OPTION_NO_MIPS3D:
- mips_opts.ase_mips3d = 0;
- break;
-
- case OPTION_SMARTMIPS:
- mips_opts.ase_smartmips = 1;
- break;
-
- case OPTION_NO_SMARTMIPS:
- mips_opts.ase_smartmips = 0;
- break;
-
case OPTION_FIX_24K:
mips_fix_24k = 1;
break;
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;
mips_relax_branch = 0;
break;
+ case OPTION_INSN32:
+ file_mips_opts.insn32 = TRUE;
+ break;
+
+ case OPTION_NO_INSN32:
+ file_mips_opts.insn32 = FALSE;
+ break;
+
case OPTION_MSHARED:
mips_in_shared = TRUE;
break;
break;
case OPTION_MSYM32:
- mips_opts.sym32 = TRUE;
+ file_mips_opts.sym32 = TRUE;
break;
case OPTION_MNO_SYM32:
- mips_opts.sym32 = FALSE;
+ file_mips_opts.sym32 = FALSE;
break;
-#ifdef OBJ_ELF
/* When generating ELF code, we permit -KPIC and -call_shared to
select SVR4_PIC, and -non_shared to select no PIC. This is
intended to be compatible with Irix 5. */
case OPTION_CALL_SHARED:
- if (!IS_ELF)
- {
- as_bad (_("-call_shared is supported only for ELF format"));
- return 0;
- }
mips_pic = SVR4_PIC;
mips_abicalls = TRUE;
break;
case OPTION_CALL_NONPIC:
- if (!IS_ELF)
- {
- as_bad (_("-call_nonpic is supported only for ELF format"));
- return 0;
- }
mips_pic = NO_PIC;
mips_abicalls = TRUE;
break;
case OPTION_NON_SHARED:
- if (!IS_ELF)
- {
- as_bad (_("-non_shared is supported only for ELF format"));
- return 0;
- }
mips_pic = NO_PIC;
mips_abicalls = FALSE;
break;
case OPTION_XGOT:
mips_big_got = 1;
break;
-#endif /* OBJ_ELF */
case 'G':
g_switch_value = atoi (arg);
/* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32
and -mabi=64. */
case OPTION_32:
- if (IS_ELF)
- mips_abi = O32_ABI;
- /* We silently ignore -32 for non-ELF targets. This greatly
- simplifies the construction of the MIPS GAS test cases. */
+ mips_abi = O32_ABI;
break;
-#ifdef OBJ_ELF
case OPTION_N32:
- if (!IS_ELF)
- {
- as_bad (_("-n32 is supported for ELF format only"));
- return 0;
- }
mips_abi = N32_ABI;
break;
case OPTION_64:
- if (!IS_ELF)
- {
- as_bad (_("-64 is supported for ELF format only"));
- return 0;
- }
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;
-#endif /* OBJ_ELF */
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_FPXX:
+ file_mips_opts.fp = 0;
break;
case OPTION_FP64:
- file_mips_fp32 = 0;
+ file_mips_opts.fp = 64;
+ break;
+
+ case OPTION_ODD_SPREG:
+ file_mips_opts.oddspreg = 1;
+ break;
+
+ case OPTION_NO_ODD_SPREG:
+ file_mips_opts.oddspreg = 0;
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;
-#ifdef OBJ_ELF
case OPTION_MABI:
- if (!IS_ELF)
- {
- as_bad (_("-mabi is supported for ELF format only"));
- return 0;
- }
if (strcmp (arg, "32") == 0)
mips_abi = O32_ABI;
else if (strcmp (arg, "o64") == 0)
{
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)
return 0;
}
break;
-#endif /* OBJ_ELF */
case OPTION_M7000_HILO_FIX:
mips_7000_hilo_fix = TRUE;
mips_7000_hilo_fix = FALSE;
break;
-#ifdef OBJ_ELF
case OPTION_MDEBUG:
mips_flag_mdebug = TRUE;
break;
case OPTION_MVXWORKS_PIC:
mips_pic = VXWORKS_PIC;
break;
-#endif /* OBJ_ELF */
+
+ case OPTION_NAN:
+ if (strcmp (arg, "2008") == 0)
+ mips_nan2008 = 1;
+ else if (strcmp (arg, "legacy") == 0)
+ mips_nan2008 = 0;
+ else
+ {
+ as_fatal (_("invalid NaN setting -mnan=%s"), arg);
+ return 0;
+ }
+ break;
default:
return 0;
return 1;
}
\f
-/* Set up globals to generate code for the ISA or processor
- described by INFO. */
-
-static void
-mips_set_architecture (const struct mips_cpu_info *info)
-{
- if (info != 0)
- {
- file_mips_arch = info->cpu;
- mips_opts.arch = info->cpu;
- mips_opts.isa = info->isa;
- }
-}
-
-
-/* Likewise for tuning. */
+/* Set up globals to tune for the ISA or processor described by INFO. */
static void
mips_set_tune (const struct mips_cpu_info *info)
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_abi == NO_ABI)
mips_abi = MIPS_DEFAULT_ABI;
- /* The following code determines the architecture and register size.
+ /* The following code determines the architecture.
Similar code was added to GCC 3.3 (see override_options() in
config/mips/mips.c). The GAS and GCC code should be kept in sync
as much as possible. */
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)
- arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+ {
+ arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+ gas_assert (arch_info);
+ }
if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (arch_info->isa))
as_bad (_("-march=%s is not compatible with the selected ABI"),
arch_info->name);
- mips_set_architecture (arch_info);
+ file_mips_opts.arch = arch_info->cpu;
+ file_mips_opts.isa = arch_info->isa;
+
+ /* Set up initial mips_opts state. */
+ mips_opts = file_mips_opts;
- /* Optimize for file_mips_arch, unless -mtune selects a different processor. */
+ /* The register size inference code is now placed in
+ file_mips_check_options. */
+
+ /* 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)
- {
- /* 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))
- as_bad (_("-mgp64 used with a 32-bit processor"));
- else if (file_mips_gp32 == 1 && 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))
- 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));
- }
-
- switch (file_mips_fp32)
- {
- default:
- case -1:
- /* No user specified float register size.
- ??? GAS treats single-float processors as though they had 64-bit
- float registers (although it complains when double-precision
- instructions are used). As things stand, saying they have 32-bit
- 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)
- /* 64-bit integer registers implies 64-bit float registers. */
- file_mips_fp32 = 0;
- else if ((mips_opts.ase_mips3d > 0 || mips_opts.ase_mdmx > 0)
- && ISA_HAS_64BIT_FPRS (mips_opts.isa))
- /* -mips3d and -mdmx imply 64-bit float registers, if possible. */
- file_mips_fp32 = 0;
- else
- /* 32-bit float registers. */
- file_mips_fp32 = 1;
- break;
-
- /* The user specified the size of the float registers. Check if it
- agrees with the ABI and ISA. */
- case 0:
- 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:
- if (ABI_NEEDS_64BIT_REGS (mips_abi))
- as_warn (_("-mfp32 used with a 64-bit ABI"));
- break;
- }
-
- /* End of GCC-shared inference code. */
-
- /* 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 == O32_ABI))
- mips_32bitmode = 1;
-
- if (mips_opts.isa == ISA_MIPS1 && mips_trap)
- as_bad (_("trap exception not supported at ISA 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;
- if (mips_opts.micromips == -1)
- mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
- if (mips_opts.ase_mips3d == -1)
- mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D)
- && file_mips_fp32 == 0) ? 1 : 0;
- if (mips_opts.ase_mips3d && file_mips_fp32 == 1)
- as_bad (_("-mfp32 used with -mips3d"));
-
- if (mips_opts.ase_mdmx == -1)
- mips_opts.ase_mdmx = ((arch_info->flags & MIPS_CPU_ASE_MDMX)
- && file_mips_fp32 == 0) ? 1 : 0;
- if (mips_opts.ase_mdmx && file_mips_fp32 == 1)
- as_bad (_("-mfp32 used with -mdmx"));
-
- if (mips_opts.ase_smartmips == -1)
- mips_opts.ase_smartmips = (arch_info->flags & MIPS_CPU_ASE_SMARTMIPS) ? 1 : 0;
- if (mips_opts.ase_smartmips && !ISA_SUPPORTS_SMARTMIPS)
- as_warn (_("%s ISA does not support SmartMIPS"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
-
- if (mips_opts.ase_dsp == -1)
- mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0;
- if (mips_opts.ase_dsp && !ISA_SUPPORTS_DSP_ASE)
- as_warn (_("%s ISA does not support DSP ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
-
- if (mips_opts.ase_dspr2 == -1)
- {
- mips_opts.ase_dspr2 = (arch_info->flags & MIPS_CPU_ASE_DSPR2) ? 1 : 0;
- mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0;
- }
- if (mips_opts.ase_dspr2 && !ISA_SUPPORTS_DSPR2_ASE)
- as_warn (_("%s ISA does not support DSP R2 ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
-
- if (mips_opts.ase_mt == -1)
- mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0;
- if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE)
- as_warn (_("%s ISA does not support MT ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
-
- if (mips_opts.ase_mcu == -1)
- mips_opts.ase_mcu = (arch_info->flags & MIPS_CPU_ASE_MCU) ? 1 : 0;
- if (mips_opts.ase_mcu && !ISA_SUPPORTS_MCU_ASE)
- as_warn (_("%s ISA does not support MCU ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
-
- file_mips_isa = mips_opts.isa;
- file_ase_mips3d = mips_opts.ase_mips3d;
- file_ase_mdmx = mips_opts.ase_mdmx;
- file_ase_smartmips = mips_opts.ase_smartmips;
- file_ase_dsp = mips_opts.ase_dsp;
- file_ase_dspr2 = mips_opts.ase_dspr2;
- file_ase_mt = mips_opts.ase_mt;
- 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;
-
if (mips_flag_mdebug < 0)
- {
-#ifdef OBJ_MAYBE_ECOFF
- if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour)
- mips_flag_mdebug = 1;
- else
-#endif /* OBJ_MAYBE_ECOFF */
- mips_flag_mdebug = 0;
- }
+ mips_flag_mdebug = 0;
}
\f
void
case BFD_RELOC_MICROMIPS_16_PCREL_S1:
case BFD_RELOC_MICROMIPS_JMP:
+ case BFD_RELOC_MIPS16_16_PCREL_S1:
case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
case BFD_RELOC_MIPS_JMP:
/* Return the address of the delay slot. */
return addr + 4;
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ /* Return the aligned address of the doubleword containing
+ the instruction. */
+ return addr & ~7;
+
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;
}
}
gas_assert (reloc_needs_lo_p (l->fixp->fx_r_type));
/* If a GOT16 relocation turns out to be against a global symbol,
- there isn't supposed to be a matching LO. */
+ there isn't supposed to be a matching LO. Ignore %gots against
+ constants; we'll report an error for those later. */
if (got16_reloc_p (l->fixp->fx_r_type)
- && !pic_need_relax (l->fixp->fx_addsy, l->seg))
+ && !(l->fixp->fx_addsy
+ && pic_need_relax (l->fixp->fx_addsy, l->seg)))
continue;
/* Check quickly whether the next fixup happens to be a matching %lo. */
matched_lo_p = FALSE;
looking_for_rtype = matching_lo_reloc (l->fixp->fx_r_type);
- for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next)
- {
- if (*pos == l->fixp)
- hi_pos = pos;
+ for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next)
+ {
+ if (*pos == l->fixp)
+ hi_pos = pos;
+
+ if ((*pos)->fx_r_type == looking_for_rtype
+ && symbol_same_p ((*pos)->fx_addsy, l->fixp->fx_addsy)
+ && (*pos)->fx_offset >= l->fixp->fx_offset
+ && (lo_pos == NULL
+ || (*pos)->fx_offset < (*lo_pos)->fx_offset
+ || (!matched_lo_p
+ && (*pos)->fx_offset == (*lo_pos)->fx_offset)))
+ lo_pos = pos;
+
+ matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type)
+ && fixup_has_matching_lo_p (*pos));
+ }
+
+ /* If we found a match, remove the high-part relocation from its
+ current position and insert it before the low-part relocation.
+ Make the offsets match so that fixup_has_matching_lo_p()
+ will return true.
+
+ We don't warn about unmatched high-part relocations since some
+ versions of gcc have been known to emit dead "lui ...%hi(...)"
+ instructions. */
+ if (lo_pos != NULL)
+ {
+ l->fixp->fx_offset = (*lo_pos)->fx_offset;
+ if (l->fixp->fx_next != *lo_pos)
+ {
+ *hi_pos = l->fixp->fx_next;
+ l->fixp->fx_next = *lo_pos;
+ *lo_pos = l->fixp;
+ }
+ }
+ }
+}
+
+int
+mips_force_relocation (fixS *fixp)
+{
+ if (generic_force_reloc (fixp))
+ return 1;
+
+ /* We want to keep BFD_RELOC_MICROMIPS_*_PCREL_S1 relocation,
+ so that the linker relaxation can update targets. */
+ if (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)
+ return 1;
+
+ /* We want to keep BFD_RELOC_16_PCREL_S2 BFD_RELOC_MIPS_21_PCREL_S2
+ and BFD_RELOC_MIPS_26_PCREL_S2 relocations against MIPS16 and
+ microMIPS symbols so that we can do cross-mode branch diagnostics
+ and BAL to JALX conversion by the linker. */
+ if ((fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2)
+ && fixp->fx_addsy
+ && ELF_ST_IS_COMPRESSED (S_GET_OTHER (fixp->fx_addsy)))
+ return 1;
+
+ /* We want all PC-relative relocations to be kept for R6 relaxation. */
+ if (ISA_IS_R6 (file_mips_opts.isa)
+ && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+ || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+ || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
+ return 1;
+
+ return 0;
+}
+
+/* Implement TC_FORCE_RELOCATION_ABS. */
+
+bfd_boolean
+mips_force_relocation_abs (fixS *fixp)
+{
+ if (generic_force_reloc (fixp))
+ return TRUE;
+
+ /* These relocations do not have enough bits in the in-place addend
+ to hold an arbitrary absolute section's offset. */
+ if (HAVE_IN_PLACE_ADDENDS && limited_pcrel_reloc_p (fixp->fx_r_type))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Read the instruction associated with RELOC from BUF. */
+
+static unsigned int
+read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ return read_compressed_insn (buf, 4);
+ else
+ return read_insn (buf);
+}
+
+/* Write instruction INSN to BUF, given that it has been relocated
+ by RELOC. */
+
+static void
+write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
+ unsigned long insn)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ write_compressed_insn (buf, insn, 4);
+ else
+ write_insn (buf, insn);
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+ to a symbol in another ISA mode, which cannot be converted to JALX. */
+
+static bfd_boolean
+fix_bad_cross_mode_jump_p (fixS *fixP)
+{
+ unsigned long opcode;
+ int other;
+ char *buf;
+
+ if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ return FALSE;
+
+ other = S_GET_OTHER (fixP->fx_addsy);
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ return opcode != 0x1d && opcode != 0x03 && ELF_ST_IS_COMPRESSED (other);
+ case BFD_RELOC_MICROMIPS_JMP:
+ return opcode != 0x3c && opcode != 0x3d && !ELF_ST_IS_MICROMIPS (other);
+ default:
+ return FALSE;
+ }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid JALX
+ jump to a symbol in the same ISA mode. */
+
+static bfd_boolean
+fix_bad_same_mode_jalx_p (fixS *fixP)
+{
+ unsigned long opcode;
+ int other;
+ char *buf;
+
+ if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ return FALSE;
+
+ other = S_GET_OTHER (fixP->fx_addsy);
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ return opcode == 0x1d && !ELF_ST_IS_COMPRESSED (other);
+ case BFD_RELOC_MIPS16_JMP:
+ return opcode == 0x07 && ELF_ST_IS_COMPRESSED (other);
+ case BFD_RELOC_MICROMIPS_JMP:
+ return opcode == 0x3c && ELF_ST_IS_COMPRESSED (other);
+ default:
+ return FALSE;
+ }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+ to a symbol whose value plus addend is not aligned according to the
+ ultimate (after linker relaxation) jump instruction's immediate field
+ requirement, either to (1 << SHIFT), or, for jumps from microMIPS to
+ regular MIPS code, to (1 << 2). */
+
+static bfd_boolean
+fix_bad_misaligned_jump_p (fixS *fixP, int shift)
+{
+ bfd_boolean micro_to_mips_p;
+ valueT val;
+ int other;
+
+ if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ return FALSE;
+
+ other = S_GET_OTHER (fixP->fx_addsy);
+ val = S_GET_VALUE (fixP->fx_addsy) | ELF_ST_IS_COMPRESSED (other);
+ val += fixP->fx_offset;
+ micro_to_mips_p = (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+ && !ELF_ST_IS_MICROMIPS (other));
+ return ((val & ((1 << (micro_to_mips_p ? 2 : shift)) - 1))
+ != ELF_ST_IS_COMPRESSED (other));
+}
- if ((*pos)->fx_r_type == looking_for_rtype
- && symbol_same_p ((*pos)->fx_addsy, l->fixp->fx_addsy)
- && (*pos)->fx_offset >= l->fixp->fx_offset
- && (lo_pos == NULL
- || (*pos)->fx_offset < (*lo_pos)->fx_offset
- || (!matched_lo_p
- && (*pos)->fx_offset == (*lo_pos)->fx_offset)))
- lo_pos = pos;
+/* Return TRUE if the instruction pointed to by FIXP is an invalid branch
+ to a symbol whose annotation indicates another ISA mode. For absolute
+ symbols check the ISA bit instead.
- matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type)
- && fixup_has_matching_lo_p (*pos));
- }
+ We accept BFD_RELOC_16_PCREL_S2 relocations against MIPS16 and microMIPS
+ symbols or BFD_RELOC_MICROMIPS_16_PCREL_S1 relocations against regular
+ MIPS symbols and associated with BAL instructions as these instructions
+ may be be converted to JALX by the linker. */
- /* If we found a match, remove the high-part relocation from its
- current position and insert it before the low-part relocation.
- Make the offsets match so that fixup_has_matching_lo_p()
- will return true.
+static bfd_boolean
+fix_bad_cross_mode_branch_p (fixS *fixP)
+{
+ bfd_boolean absolute_p;
+ unsigned long opcode;
+ asection *symsec;
+ valueT val;
+ int other;
+ char *buf;
- We don't warn about unmatched high-part relocations since some
- versions of gcc have been known to emit dead "lui ...%hi(...)"
- instructions. */
- if (lo_pos != NULL)
- {
- l->fixp->fx_offset = (*lo_pos)->fx_offset;
- if (l->fixp->fx_next != *lo_pos)
- {
- *hi_pos = l->fixp->fx_next;
- l->fixp->fx_next = *lo_pos;
- *lo_pos = l->fixp;
- }
- }
+ if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ return FALSE;
+
+ symsec = S_GET_SEGMENT (fixP->fx_addsy);
+ absolute_p = bfd_is_abs_section (symsec);
+
+ val = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset;
+ other = S_GET_OTHER (fixP->fx_addsy);
+
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 16;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ return ((absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other))
+ && opcode != 0x0411);
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ return ((absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other))
+ && opcode != 0x4060);
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ return absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other);
+ case BFD_RELOC_MIPS16_16_PCREL_S1:
+ return absolute_p ? !(val & 1) : !ELF_ST_IS_MIPS16 (other);
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ return absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other);
+ default:
+ abort ();
}
}
-/* We may have combined relocations without symbols in the N32/N64 ABI.
- We have to prevent gas from dropping them. */
+/* Return TRUE if the symbol plus addend associated with a regular MIPS
+ branch instruction pointed to by FIXP is not aligned according to the
+ branch instruction's immediate field requirement. We need the addend
+ to preserve the ISA bit and also the sum must not have bit 2 set. We
+ must explicitly OR in the ISA bit from symbol annotation as the bit
+ won't be set in the symbol's value then. */
-int
-mips_force_relocation (fixS *fixp)
+static bfd_boolean
+fix_bad_misaligned_branch_p (fixS *fixP)
{
- if (generic_force_reloc (fixp))
- return 1;
+ bfd_boolean absolute_p;
+ asection *symsec;
+ valueT isa_bit;
+ valueT val;
+ valueT off;
+ int other;
- /* We want to keep BFD_RELOC_MICROMIPS_*_PCREL_S1 relocation,
- so that the linker relaxation can update targets. */
- if (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)
- return 1;
+ if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ return FALSE;
- if (HAVE_NEWABI
- && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
- && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
- || hi16_reloc_p (fixp->fx_r_type)
- || lo16_reloc_p (fixp->fx_r_type)))
- return 1;
+ symsec = S_GET_SEGMENT (fixP->fx_addsy);
+ absolute_p = bfd_is_abs_section (symsec);
- return 0;
+ val = S_GET_VALUE (fixP->fx_addsy);
+ other = S_GET_OTHER (fixP->fx_addsy);
+ off = fixP->fx_offset;
+
+ isa_bit = absolute_p ? (val + off) & 1 : ELF_ST_IS_COMPRESSED (other);
+ val |= ELF_ST_IS_COMPRESSED (other);
+ val += off;
+ return (val & 0x3) != isa_bit;
+}
+
+/* Make the necessary checks on a regular MIPS branch pointed to by FIXP
+ and its calculated value VAL. */
+
+static void
+fix_validate_branch (fixS *fixP, valueT val)
+{
+ if (fixP->fx_done && (val & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to misaligned address (0x%lx)"),
+ (long) (val + md_pcrel_from (fixP)));
+ else if (fix_bad_cross_mode_branch_p (fixP))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to a symbol in another ISA mode"));
+ else if (fix_bad_misaligned_branch_p (fixP))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to misaligned address (0x%lx)"),
+ (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+ else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot encode misaligned addend "
+ "in the relocatable field (0x%lx)"),
+ (long) fixP->fx_offset);
}
/* Apply a fixup to the object file. */
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
- bfd_byte *buf;
- long insn;
+ char *buf;
+ 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_MIPS16_16_PCREL_S1:
+ 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:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ case BFD_RELOC_MIPS_19_PCREL_S2:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_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
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
|| fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
- || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
+ || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64
+ || fixP->fx_r_type == BFD_RELOC_NONE);
- buf = (bfd_byte *) (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);
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
/* Don't treat parts of a composite relocation as done. There are two
reasons for this:
case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
case BFD_RELOC_MIPS_TLS_GOTTPREL:
+ case BFD_RELOC_MIPS_TLS_TPREL32:
+ case BFD_RELOC_MIPS_TLS_TPREL64:
case BFD_RELOC_MIPS_TLS_TPREL_HI16:
case BFD_RELOC_MIPS_TLS_TPREL_LO16:
case BFD_RELOC_MICROMIPS_TLS_GD:
case BFD_RELOC_MICROMIPS_TLS_GOTTPREL:
case BFD_RELOC_MICROMIPS_TLS_TPREL_HI16:
case BFD_RELOC_MICROMIPS_TLS_TPREL_LO16:
- S_SET_THREAD_LOCAL (fixP->fx_addsy);
- /* fall through */
+ case BFD_RELOC_MIPS16_TLS_GD:
+ case BFD_RELOC_MIPS16_TLS_LDM:
+ case BFD_RELOC_MIPS16_TLS_DTPREL_HI16:
+ case BFD_RELOC_MIPS16_TLS_DTPREL_LO16:
+ case BFD_RELOC_MIPS16_TLS_GOTTPREL:
+ case BFD_RELOC_MIPS16_TLS_TPREL_HI16:
+ case BFD_RELOC_MIPS16_TLS_TPREL_LO16:
+ if (fixP->fx_addsy)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("TLS relocation against a constant"));
+ break;
case BFD_RELOC_MIPS_JMP:
+ case BFD_RELOC_MIPS16_JMP:
+ case BFD_RELOC_MICROMIPS_JMP:
+ {
+ int shift;
+
+ gas_assert (!fixP->fx_done);
+
+ /* Shift is 2, unusually, for microMIPS JALX. */
+ if (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+ && (read_compressed_insn (buf, 4) >> 26) != 0x3c)
+ shift = 1;
+ else
+ shift = 2;
+
+ if (fix_bad_cross_mode_jump_p (fixP))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("jump to a symbol in another ISA mode"));
+ else if (fix_bad_same_mode_jalx_p (fixP))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("JALX to a symbol in the same ISA mode"));
+ else if (fix_bad_misaligned_jump_p (fixP, shift))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("jump to misaligned address (0x%lx)"),
+ (long) (S_GET_VALUE (fixP->fx_addsy)
+ + fixP->fx_offset));
+ else if (HAVE_IN_PLACE_ADDENDS
+ && (fixP->fx_offset & ((1 << shift) - 1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot encode misaligned addend "
+ "in the relocatable field (0x%lx)"),
+ (long) fixP->fx_offset);
+ }
+ /* Fall through. */
+
case BFD_RELOC_MIPS_SHIFT5:
case BFD_RELOC_MIPS_SHIFT6:
case BFD_RELOC_MIPS_GOT_DISP:
case BFD_RELOC_MIPS_JALR:
case BFD_RELOC_HI16:
case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
case BFD_RELOC_GPREL16:
case BFD_RELOC_MIPS_LITERAL:
case BFD_RELOC_MIPS_CALL16:
case BFD_RELOC_MIPS_GOT_LO16:
case BFD_RELOC_MIPS_CALL_HI16:
case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_PCREL:
case BFD_RELOC_MIPS16_GPREL:
case BFD_RELOC_MIPS16_GOT16:
case BFD_RELOC_MIPS16_CALL16:
case BFD_RELOC_MIPS16_HI16:
case BFD_RELOC_MIPS16_HI16_S:
- case BFD_RELOC_MIPS16_JMP:
- case BFD_RELOC_MICROMIPS_JMP:
+ case BFD_RELOC_MIPS16_LO16:
case BFD_RELOC_MICROMIPS_GOT_DISP:
case BFD_RELOC_MICROMIPS_GOT_PAGE:
case BFD_RELOC_MICROMIPS_GOT_OFST:
case BFD_RELOC_MICROMIPS_JALR:
case BFD_RELOC_MICROMIPS_HI16:
case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MICROMIPS_LO16:
case BFD_RELOC_MICROMIPS_GPREL16:
case BFD_RELOC_MICROMIPS_LITERAL:
case BFD_RELOC_MICROMIPS_CALL16:
case BFD_RELOC_MICROMIPS_GOT_LO16:
case BFD_RELOC_MICROMIPS_CALL_HI16:
case BFD_RELOC_MICROMIPS_CALL_LO16:
- /* Nothing needed to do. The value comes from the reloc entry. */
+ case BFD_RELOC_MIPS_EH:
+ if (fixP->fx_done)
+ {
+ offsetT value;
+
+ if (calculate_reloc (fixP->fx_r_type, *valP, &value))
+ {
+ insn = read_reloc_insn (buf, fixP->fx_r_type);
+ if (mips16_reloc_p (fixP->fx_r_type))
+ insn |= mips16_immed_extend (value, 16);
+ else
+ insn |= (value & 0xffff);
+ write_reloc_insn (buf, fixP->fx_r_type, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported constant in relocation"));
+ }
break;
case BFD_RELOC_64:
if (fixP->fx_done)
{
if (8 <= sizeof (valueT))
- md_number_to_chars ((char *) buf, *valP, 8);
+ md_number_to_chars (buf, *valP, 8);
else
{
valueT hiv;
hiv = 0xffffffff;
else
hiv = 0;
- md_number_to_chars ((char *)(buf + (target_big_endian ? 4 : 0)),
- *valP, 4);
- md_number_to_chars ((char *)(buf + (target_big_endian ? 0 : 4)),
- hiv, 4);
+ md_number_to_chars (buf + (target_big_endian ? 4 : 0), *valP, 4);
+ md_number_to_chars (buf + (target_big_endian ? 0 : 4), hiv, 4);
}
}
break;
case BFD_RELOC_RVA:
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. */
if (fixP->fx_done)
- md_number_to_chars ((char *) buf, *valP, fixP->fx_size);
+ md_number_to_chars (buf, *valP, fixP->fx_size);
break;
- case BFD_RELOC_LO16:
- case BFD_RELOC_MIPS16_LO16:
- case BFD_RELOC_MICROMIPS_LO16:
- /* FIXME: Now that embedded-PIC is gone, some of this code/comment
- may be safe to remove, but if so it's not obvious. */
- /* When handling an embedded PIC switch statement, we can wind
- up deleting a LO16 reloc. See the 'o' case in mips_ip. */
- if (fixP->fx_done)
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ fix_validate_branch (fixP, *valP);
+ if (!fixP->fx_done)
+ break;
+
+ if (*valP + 0x400000 <= 0x7fffff)
{
- if (*valP + 0x8000 > 0xffff)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("relocation overflow"));
- /* 32-bit microMIPS instructions are divided into two halfwords.
- Relocations always refer to the second halfword, regardless
- of endianness. */
- if (target_big_endian || fixP->fx_r_type == BFD_RELOC_MICROMIPS_LO16)
- buf += 2;
- md_number_to_chars ((char *) buf, *valP, 2);
+ insn = read_insn (buf);
+ insn |= (*valP >> 2) & 0x1fffff;
+ write_insn (buf, insn);
}
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
break;
- case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ fix_validate_branch (fixP, *valP);
+ if (!fixP->fx_done)
+ break;
+
+ if (*valP + 0x8000000 <= 0xfffffff)
+ {
+ insn = read_insn (buf);
+ insn |= (*valP >> 2) & 0x3ffffff;
+ write_insn (buf, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+ break;
+
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ if (fixP->fx_addsy && (S_GET_VALUE (fixP->fx_addsy) & 0x7) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access using misaligned symbol (%lx)"),
+ (long) S_GET_VALUE (fixP->fx_addsy));
+ if ((fixP->fx_offset & 0x7) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access using misaligned offset (%lx)"),
+ (long) fixP->fx_offset);
+ if (!fixP->fx_done)
+ break;
+
+ if (*valP + 0x100000 <= 0x1fffff)
+ {
+ insn = read_insn (buf);
+ insn |= (*valP >> 3) & 0x3ffff;
+ write_insn (buf, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access out of range"));
+ break;
+
+ case BFD_RELOC_MIPS_19_PCREL_S2:
if ((*valP & 0x3) != 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Branch to misaligned address (%lx)"), (long) *valP);
+ _("PC-relative access to misaligned address (%lx)"),
+ (long) *valP);
+ if (!fixP->fx_done)
+ break;
+
+ if (*valP + 0x100000 <= 0x1fffff)
+ {
+ insn = read_insn (buf);
+ insn |= (*valP >> 2) & 0x7ffff;
+ write_insn (buf, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access out of range"));
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ fix_validate_branch (fixP, *valP);
/* We need to save the bits in the instruction since fixup_segment()
might be deleting the relocation entry (i.e., a branch within
break;
/* Update old instruction data. */
- if (target_big_endian)
- insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
- else
- insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ insn = read_insn (buf);
if (*valP + 0x20000 <= 0x3ffff)
{
insn |= (*valP >> 2) & 0xffff;
- md_number_to_chars ((char *) buf, insn, 4);
+ write_insn (buf, insn);
}
else if (mips_pic == NO_PIC
&& fixP->fx_done
fixP->fx_done = 0;
fixP->fx_addsy = section_symbol (text_section);
*valP += md_pcrel_from (fixP);
- md_number_to_chars ((char *) buf, insn, 4);
+ write_insn (buf, insn);
}
else
{
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;
+ case BFD_RELOC_MIPS16_16_PCREL_S1:
case BFD_RELOC_MICROMIPS_7_PCREL_S1:
case BFD_RELOC_MICROMIPS_10_PCREL_S1:
case BFD_RELOC_MICROMIPS_16_PCREL_S1:
- /* We adjust the offset back to even. */
- if ((*valP & 0x1) != 0)
- --(*valP);
-
- if (! fixP->fx_done)
- break;
-
- /* Should never visit here, because we keep the relocation. */
- abort ();
+ gas_assert (!fixP->fx_done);
+ if (fix_bad_cross_mode_branch_p (fixP))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to a symbol in another ISA mode"));
+ else if (fixP->fx_addsy
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && !bfd_is_abs_section (S_GET_SEGMENT (fixP->fx_addsy))
+ && (fixP->fx_offset & 0x1) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to misaligned address (0x%lx)"),
+ (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+ else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x1) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot encode misaligned addend "
+ "in the relocatable field (0x%lx)"),
+ (long) fixP->fx_offset);
break;
case BFD_RELOC_VTABLE_INHERIT:
S_SET_WEAK (fixP->fx_addsy);
break;
+ case BFD_RELOC_NONE:
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
break;
default:
- internalError ();
+ abort ();
}
/* Remember value for tc_gen_reloc. */
char *name;
symbolS *p;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
p = (symbolS *) symbol_find_or_make (name);
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
return p;
}
fill byte should be used, FILL points to an integer that contains
that byte, otherwise FILL is null.
- The MIPS assembler also automatically adjusts any preceding
- label. */
+ This function used to have the comment:
+
+ The MIPS assembler also automatically adjusts any preceding label.
+
+ The implementation therefore applied the adjustment to a maximum of
+ one label. However, other label adjustments are applied to batches
+ of labels, and adjusting just one caused problems when new labels
+ were added for the sake of debugging or unwind information.
+ We therefore adjust all preceding labels (given as LABELS) instead. */
static void
-mips_align (int to, int *fill, symbolS *label)
+mips_align (int to, int *fill, struct insn_label_list *labels)
{
mips_emit_delays ();
mips_record_compressed_mode ();
else
frag_align (to, fill ? *fill : 0, 0);
record_alignment (now_seg, to);
- if (label != NULL)
- {
- gas_assert (S_GET_SEGMENT (label) == now_seg);
- symbol_set_frag (label, frag_now);
- S_SET_VALUE (label, (valueT) frag_now_fix ());
- }
+ mips_move_labels (labels, FALSE);
}
/* Align to a given power of two. .align 0 turns off the automatic
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 == ',')
struct insn_label_list *l = si->label_list;
/* Auto alignment should be switched on by next section change. */
auto_align = 1;
- mips_align (temp, fill_ptr, l != NULL ? l->label : NULL);
+ mips_align (temp, fill_ptr, l);
}
else
{
{
segT seg;
-#ifdef OBJ_ELF
/* The ELF backend needs to know that we are changing sections, so
that .previous works correctly. We could do something like check
for an obj_section_change_hook macro, but that might be confusing
as it would not be appropriate to use it in the section changing
functions in read.c, since obj-elf.c intercepts those. FIXME:
This should be cleaner, somehow. */
- if (IS_ELF)
- obj_elf_section_change_hook ();
-#endif
+ obj_elf_section_change_hook ();
mips_emit_delays ();
case 'r':
seg = subseg_new (RDATA_SECTION_NAME,
(subsegT) get_absolute_expression ());
- if (IS_ELF)
- {
- bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
- | SEC_READONLY | SEC_RELOC
- | SEC_DATA));
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- record_alignment (seg, 4);
- }
+ bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY | SEC_RELOC
+ | SEC_DATA));
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
demand_empty_rest_of_line ();
break;
case 's':
seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
- if (IS_ELF)
- {
- bfd_set_section_flags (stdoutput, seg,
- SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- record_alignment (seg, 4);
- }
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
demand_empty_rest_of_line ();
break;
case 'B':
seg = subseg_new (".sbss", (subsegT) get_absolute_expression ());
- if (IS_ELF)
- {
- bfd_set_section_flags (stdoutput, seg, SEC_ALLOC);
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- record_alignment (seg, 4);
- }
+ bfd_set_section_flags (stdoutput, seg, SEC_ALLOC);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
demand_empty_rest_of_line ();
break;
}
void
s_change_section (int ignore ATTRIBUTE_UNUSED)
{
-#ifdef OBJ_ELF
+ char *saved_ilp;
char *section_name;
- char c;
+ char c, endc;
char next_c = 0;
int section_type;
int section_flag;
int section_entry_size;
int section_alignment;
- if (!IS_ELF)
- return;
-
- section_name = input_line_pointer;
- c = get_symbol_end ();
+ saved_ilp = input_line_pointer;
+ endc = get_symbol_name (§ion_name);
+ c = (endc == '"' ? input_line_pointer[1] : endc);
if (c)
- next_c = *(input_line_pointer + 1);
+ next_c = input_line_pointer [(endc == '"' ? 2 : 1)];
/* Do we have .section Name<,"flags">? */
if (c != ',' || (c == ',' && next_c == '"'))
{
- /* just after name is now '\0'. */
- *input_line_pointer = c;
- input_line_pointer = section_name;
+ /* Just after name is now '\0'. */
+ (void) restore_line_pointer (endc);
+ input_line_pointer = saved_ilp;
obj_elf_section (ignore);
return;
}
+
+ section_name = xstrdup (section_name);
+ c = restore_line_pointer (endc);
+
input_line_pointer++;
/* Do we have .section Name<,type><,flag><,entry_size><,alignment> */
section_type = get_absolute_expression ();
else
section_type = 0;
+
if (*input_line_pointer++ == ',')
section_flag = get_absolute_expression ();
else
section_flag = 0;
+
if (*input_line_pointer++ == ',')
section_entry_size = get_absolute_expression ();
else
section_entry_size = 0;
+
if (*input_line_pointer++ == ',')
section_alignment = get_absolute_expression ();
else
section_alignment = 0;
+
/* FIXME: really ignore? */
(void) section_alignment;
- section_name = xstrdup (section_name);
-
/* When using the generic form of .section (as implemented by obj-elf.c),
there's no way to set the section type to SHT_MIPS_DWARF. Users have
traditionally had to fall back on the more common @progbits instead.
if (now_seg->name != section_name)
free (section_name);
-#endif /* OBJ_ELF */
}
void
{
segment_info_type *si = seg_info (now_seg);
struct insn_label_list *l = si->label_list;
- symbolS *label;
- label = l != NULL ? l->label : NULL;
mips_emit_delays ();
if (log_size > 0 && auto_align)
- mips_align (log_size, 0, label);
+ mips_align (log_size, 0, l);
cons (1 << log_size);
mips_clear_insn_labels ();
}
{
segment_info_type *si = seg_info (now_seg);
struct insn_label_list *l = si->label_list;
- symbolS *label;
-
- label = l != NULL ? l->label : NULL;
mips_emit_delays ();
if (auto_align)
{
if (type == 'd')
- mips_align (3, 0, label);
+ mips_align (3, 0, l);
else
- mips_align (2, 0, label);
+ mips_align (2, 0, l);
}
float_cons (type);
do
{
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
symbolP = symbol_find_or_make (name);
S_SET_EXTERNAL (symbolP);
*input_line_pointer = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
/* On Irix 5, every global symbol that is not explicitly labelled as
being a function is apparently labelled as being an object. */
char *secname;
asection *sec;
- secname = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&secname);
sec = bfd_get_section_by_name (stdoutput, secname);
if (sec == NULL)
as_bad (_("%s: no such section"), secname);
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
if (sec != NULL && (sec->flags & SEC_CODE) != 0)
flag = BSF_FUNCTION;
char *opt;
char c;
- opt = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&opt);
if (*opt == 'O')
{
/* FIXME: What does this mean? */
}
- else if (strncmp (opt, "pic", 3) == 0)
+ else if (strncmp (opt, "pic", 3) == 0 && ISDIGIT (opt[3]) && opt[4] == '\0')
{
int i;
i = atoi (opt + 3);
- if (i == 0)
+ if (i != 0 && i != 2)
+ as_bad (_(".option pic%d not supported"), i);
+ else if (mips_pic == VXWORKS_PIC)
+ as_bad (_(".option pic%d not supported in VxWorks PIC mode"), i);
+ else if (i == 0)
mips_pic = NO_PIC;
else if (i == 2)
{
mips_pic = SVR4_PIC;
mips_abicalls = TRUE;
}
- else
- as_bad (_(".option pic%d not supported"), i);
if (mips_pic == SVR4_PIC)
{
}
}
else
- as_warn (_("Unrecognized option \"%s\""), opt);
+ as_warn (_("unrecognized option \"%s\""), opt);
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
demand_empty_rest_of_line ();
}
static struct mips_option_stack *mips_opts_stack;
-/* Handle the .set pseudo-op. */
+/* Return status for .set/.module option handling. */
-static void
-s_mipsset (int x ATTRIBUTE_UNUSED)
+enum code_option_type
{
- char *name = input_line_pointer, ch;
+ /* Unrecognized option. */
+ OPTION_TYPE_BAD = -1,
- while (!is_end_of_line[(unsigned char) *input_line_pointer])
- ++input_line_pointer;
- ch = *input_line_pointer;
- *input_line_pointer = '\0';
+ /* Ordinary option. */
+ OPTION_TYPE_NORMAL,
- if (strcmp (name, "reorder") == 0)
- {
- if (mips_opts.noreorder)
- end_noreorder ();
- }
- else if (strcmp (name, "noreorder") == 0)
- {
- if (!mips_opts.noreorder)
- start_noreorder ();
- }
- else if (strncmp (name, "at=", 3) == 0)
+ /* ISA changing option. */
+ OPTION_TYPE_ISA
+};
+
+/* Handle common .set/.module options. Return status indicating option
+ type. */
+
+static enum code_option_type
+parse_code_option (char * name)
+{
+ bfd_boolean isa_set = FALSE;
+ const struct mips_ase *ase;
+
+ if (strncmp (name, "at=", 3) == 0)
{
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.at = ATREG;
- }
+ mips_opts.at = ATREG;
else if (strcmp (name, "noat") == 0)
- {
- mips_opts.at = ZERO;
- }
- else if (strcmp (name, "macro") == 0)
- {
- mips_opts.warn_about_macros = 0;
- }
- else if (strcmp (name, "nomacro") == 0)
- {
- if (mips_opts.noreorder == 0)
- as_bad (_("`noreorder' must be set before `nomacro'"));
- mips_opts.warn_about_macros = 1;
- }
+ mips_opts.at = ZERO;
else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
- {
- mips_opts.nomove = 0;
- }
+ mips_opts.nomove = 0;
else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
- {
- mips_opts.nomove = 1;
- }
+ mips_opts.nomove = 1;
else if (strcmp (name, "bopt") == 0)
- {
- mips_opts.nobopt = 0;
- }
+ mips_opts.nobopt = 0;
else if (strcmp (name, "nobopt") == 0)
- {
- mips_opts.nobopt = 1;
- }
- else if (strcmp (name, "gp=default") == 0)
- mips_opts.gp32 = file_mips_gp32;
+ mips_opts.nobopt = 1;
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;
- }
- else if (strcmp (name, "fp=default") == 0)
- mips_opts.fp32 = file_mips_fp32;
+ mips_opts.gp = 64;
else if (strcmp (name, "fp=32") == 0)
- mips_opts.fp32 = 1;
+ mips_opts.fp = 32;
+ else if (strcmp (name, "fp=xx") == 0)
+ mips_opts.fp = 0;
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;
else if (strcmp (name, "hardfloat") == 0)
mips_opts.single_float = 1;
else if (strcmp (name, "doublefloat") == 0)
mips_opts.single_float = 0;
+ else if (strcmp (name, "nooddspreg") == 0)
+ mips_opts.oddspreg = 0;
+ else if (strcmp (name, "oddspreg") == 0)
+ mips_opts.oddspreg = 1;
else if (strcmp (name, "mips16") == 0
|| strcmp (name, "MIPS-16") == 0)
- {
- if (mips_opts.micromips == 1)
- as_fatal (_("`mips16' cannot be used with `micromips'"));
- mips_opts.mips16 = 1;
- }
+ mips_opts.mips16 = 1;
else if (strcmp (name, "nomips16") == 0
|| strcmp (name, "noMIPS-16") == 0)
mips_opts.mips16 = 0;
- else if (strcmp (name, "micromips") == 0)
- {
- if (mips_opts.mips16 == 1)
- as_fatal (_("`micromips' cannot be used with `mips16'"));
- mips_opts.micromips = 1;
- }
+ else if (strcmp (name, "micromips") == 0)
+ mips_opts.micromips = 1;
else if (strcmp (name, "nomicromips") == 0)
mips_opts.micromips = 0;
- else if (strcmp (name, "smartmips") == 0)
- {
- if (!ISA_SUPPORTS_SMARTMIPS)
- as_warn (_("%s ISA does not support SmartMIPS ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.ase_smartmips = 1;
- }
- else if (strcmp (name, "nosmartmips") == 0)
- mips_opts.ase_smartmips = 0;
- else if (strcmp (name, "mips3d") == 0)
- mips_opts.ase_mips3d = 1;
- else if (strcmp (name, "nomips3d") == 0)
- mips_opts.ase_mips3d = 0;
- else if (strcmp (name, "mdmx") == 0)
- mips_opts.ase_mdmx = 1;
- else if (strcmp (name, "nomdmx") == 0)
- mips_opts.ase_mdmx = 0;
- else if (strcmp (name, "dsp") == 0)
- {
- if (!ISA_SUPPORTS_DSP_ASE)
- as_warn (_("%s ISA does not support DSP ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.ase_dsp = 1;
- mips_opts.ase_dspr2 = 0;
- }
- else if (strcmp (name, "nodsp") == 0)
- {
- mips_opts.ase_dsp = 0;
- mips_opts.ase_dspr2 = 0;
- }
- else if (strcmp (name, "dspr2") == 0)
- {
- if (!ISA_SUPPORTS_DSPR2_ASE)
- as_warn (_("%s ISA does not support DSP R2 ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.ase_dspr2 = 1;
- mips_opts.ase_dsp = 1;
- }
- else if (strcmp (name, "nodspr2") == 0)
- {
- mips_opts.ase_dspr2 = 0;
- mips_opts.ase_dsp = 0;
- }
- else if (strcmp (name, "mt") == 0)
- {
- if (!ISA_SUPPORTS_MT_ASE)
- as_warn (_("%s ISA does not support MT ASE"),
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.ase_mt = 1;
- }
- else if (strcmp (name, "nomt") == 0)
- mips_opts.ase_mt = 0;
- else if (strcmp (name, "mcu") == 0)
- mips_opts.ase_mcu = 1;
- else if (strcmp (name, "nomcu") == 0)
- mips_opts.ase_mcu = 0;
+ else if (name[0] == 'n'
+ && name[1] == 'o'
+ && (ase = mips_lookup_ase (name + 2)))
+ mips_set_ase (ase, &mips_opts, FALSE);
+ else if ((ase = mips_lookup_ase (name)))
+ mips_set_ase (ase, &mips_opts, TRUE);
else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
{
- int reset = 0;
-
/* Permit the user to change the ISA and architecture on the fly.
Needless to say, misuse can cause serious problems. */
- if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
- {
- reset = 1;
- mips_opts.isa = file_mips_isa;
- mips_opts.arch = file_mips_arch;
- }
- else if (strncmp (name, "arch=", 5) == 0)
+ if (strncmp (name, "arch=", 5) == 0)
{
const struct mips_cpu_info *p;
- p = mips_parse_cpu("internal use", name + 5);
+ p = mips_parse_cpu ("internal use", name + 5);
if (!p)
as_bad (_("unknown architecture %s"), name + 5);
else
{
mips_opts.arch = p->cpu;
mips_opts.isa = p->isa;
+ isa_set = TRUE;
}
}
else if (strncmp (name, "mips", 4) == 0)
{
const struct mips_cpu_info *p;
- p = mips_parse_cpu("internal use", name);
+ p = mips_parse_cpu ("internal use", name);
if (!p)
as_bad (_("unknown ISA level %s"), name + 4);
else
{
mips_opts.arch = p->cpu;
mips_opts.isa = p->isa;
+ isa_set = TRUE;
}
}
else
as_bad (_("unknown ISA or architecture %s"), name);
-
- switch (mips_opts.isa)
- {
- case 0:
- break;
- case ISA_MIPS1:
- case ISA_MIPS2:
- case ISA_MIPS32:
- case ISA_MIPS32R2:
- mips_opts.gp32 = 1;
- mips_opts.fp32 = 1;
- break;
- case ISA_MIPS3:
- case ISA_MIPS4:
- case ISA_MIPS5:
- case ISA_MIPS64:
- case ISA_MIPS64R2:
- mips_opts.gp32 = 0;
- mips_opts.fp32 = 0;
- break;
- default:
- as_bad (_("unknown ISA level %s"), name + 4);
- break;
- }
- if (reset)
- {
- mips_opts.gp32 = file_mips_gp32;
- mips_opts.fp32 = file_mips_fp32;
- }
}
else if (strcmp (name, "autoextend") == 0)
mips_opts.noautoextend = 0;
else if (strcmp (name, "noautoextend") == 0)
mips_opts.noautoextend = 1;
+ else if (strcmp (name, "insn32") == 0)
+ mips_opts.insn32 = TRUE;
+ else if (strcmp (name, "noinsn32") == 0)
+ mips_opts.insn32 = FALSE;
+ else if (strcmp (name, "sym32") == 0)
+ mips_opts.sym32 = TRUE;
+ else if (strcmp (name, "nosym32") == 0)
+ mips_opts.sym32 = FALSE;
+ else
+ return OPTION_TYPE_BAD;
+
+ return isa_set ? OPTION_TYPE_ISA : OPTION_TYPE_NORMAL;
+}
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+ enum code_option_type type = OPTION_TYPE_NORMAL;
+ char *name = input_line_pointer, ch;
+
+ file_mips_check_options ();
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strchr (name, ','))
+ {
+ /* Generic ".set" directive; use the generic handler. */
+ *input_line_pointer = ch;
+ input_line_pointer = name;
+ s_set (0);
+ return;
+ }
+
+ if (strcmp (name, "reorder") == 0)
+ {
+ if (mips_opts.noreorder)
+ end_noreorder ();
+ }
+ else if (strcmp (name, "noreorder") == 0)
+ {
+ if (!mips_opts.noreorder)
+ start_noreorder ();
+ }
+ else if (strcmp (name, "macro") == 0)
+ mips_opts.warn_about_macros = 0;
+ else if (strcmp (name, "nomacro") == 0)
+ {
+ if (mips_opts.noreorder == 0)
+ as_bad (_("`noreorder' must be set before `nomacro'"));
+ mips_opts.warn_about_macros = 1;
+ }
+ else if (strcmp (name, "gp=default") == 0)
+ mips_opts.gp = file_mips_opts.gp;
+ else if (strcmp (name, "fp=default") == 0)
+ mips_opts.fp = file_mips_opts.fp;
+ else if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
+ {
+ mips_opts.isa = file_mips_opts.isa;
+ mips_opts.arch = file_mips_opts.arch;
+ mips_opts.gp = file_mips_opts.gp;
+ mips_opts.fp = file_mips_opts.fp;
+ }
else if (strcmp (name, "push") == 0)
{
struct mips_option_stack *s;
- s = (struct mips_option_stack *) xmalloc (sizeof *s);
+ s = XNEW (struct mips_option_stack);
s->next = mips_opts_stack;
s->options = mips_opts;
mips_opts_stack = s;
free (s);
}
}
- else if (strcmp (name, "sym32") == 0)
- mips_opts.sym32 = TRUE;
- else if (strcmp (name, "nosym32") == 0)
- mips_opts.sym32 = FALSE;
- else if (strchr (name, ','))
+ else
{
- /* Generic ".set" directive; use the generic handler. */
- *input_line_pointer = ch;
- input_line_pointer = name;
- s_set (0);
- return;
+ type = parse_code_option (name);
+ if (type == OPTION_TYPE_BAD)
+ as_warn (_("tried to set unrecognized symbol: %s\n"), name);
}
- else
+
+ /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp
+ registers based on what is supported by the arch/cpu. */
+ if (type == OPTION_TYPE_ISA)
+ {
+ switch (mips_opts.isa)
+ {
+ case 0:
+ break;
+ case ISA_MIPS1:
+ /* MIPS I cannot support FPXX. */
+ mips_opts.fp = 32;
+ /* fall-through. */
+ case ISA_MIPS2:
+ case ISA_MIPS32:
+ case ISA_MIPS32R2:
+ case ISA_MIPS32R3:
+ case ISA_MIPS32R5:
+ mips_opts.gp = 32;
+ if (mips_opts.fp != 0)
+ mips_opts.fp = 32;
+ break;
+ case ISA_MIPS32R6:
+ mips_opts.gp = 32;
+ mips_opts.fp = 64;
+ break;
+ case ISA_MIPS3:
+ case ISA_MIPS4:
+ case ISA_MIPS5:
+ case ISA_MIPS64:
+ case ISA_MIPS64R2:
+ case ISA_MIPS64R3:
+ case ISA_MIPS64R5:
+ case ISA_MIPS64R6:
+ mips_opts.gp = 64;
+ if (mips_opts.fp != 0)
+ {
+ if (mips_opts.arch == CPU_R5900)
+ mips_opts.fp = 32;
+ else
+ mips_opts.fp = 64;
+ }
+ break;
+ default:
+ as_bad (_("unknown ISA level %s"), name + 4);
+ break;
+ }
+ }
+
+ mips_check_options (&mips_opts, FALSE);
+
+ mips_check_isa_supports_ases ();
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .module pseudo-op. */
+
+static void
+s_module (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (!file_mips_opts_checked)
{
- as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+ if (parse_code_option (name) == OPTION_TYPE_BAD)
+ as_bad (_(".module used with unrecognized symbol: %s\n"), name);
+
+ /* Update module level settings from mips_opts. */
+ file_mips_opts = mips_opts;
}
+ else
+ as_bad (_(".module is not permitted after generating code"));
+
*input_line_pointer = ch;
demand_empty_rest_of_line ();
}
int reg;
int in_shared;
+ file_mips_check_options ();
+
/* If we are not generating SVR4 PIC code, or if this is NewABI code,
.cpload is ignored. */
if (mips_pic != SVR4_PIC || HAVE_NEWABI)
return;
}
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpload");
+ ignore_rest_of_line ();
+ return;
+ }
+
/* .cpload should be in a .set noreorder section. */
if (mips_opts.noreorder == 0)
as_warn (_(".cpload not in noreorder section"));
/* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
macro_build_lui (&ex, mips_gp_register);
macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
mips_gp_register, reg);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
daddu $gp, $gp, $reg1
If $reg2 is given, this results in:
- daddu $reg2, $gp, $0
+ or $reg2, $gp, $0
lui $gp, %hi(%neg(%gp_rel(label)))
addiu $gp, $gp, %lo(%neg(%gp_rel(label)))
daddu $gp, $gp, $reg1
expressionS ex_sym;
int reg1;
+ file_mips_check_options ();
+
/* If we are not generating SVR4 PIC code, .cpsetup is ignored.
We also need NewABI support. */
if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
return;
}
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpsetup");
+ ignore_rest_of_line ();
+ return;
+ }
+
reg1 = tc_get_register (0);
SKIP_WHITESPACE ();
if (*input_line_pointer != ',')
SKIP_WHITESPACE ();
expression (&ex_sym);
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
if (mips_cpreturn_register == -1)
{
BFD_RELOC_LO16, SP);
}
else
- macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
- mips_gp_register, 0);
+ move_register (mips_cpreturn_register, mips_gp_register);
if (mips_in_shared || HAVE_64BIT_SYMBOLS)
{
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
static void
s_cplocal (int ignore ATTRIBUTE_UNUSED)
{
+ file_mips_check_options ();
+
/* If we are not generating SVR4 PIC code, or if this is not NewABI code,
.cplocal is ignored. */
if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
return;
}
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cplocal");
+ ignore_rest_of_line ();
+ return;
+ }
+
mips_gp_register = tc_get_register (0);
demand_empty_rest_of_line ();
}
{
expressionS ex;
+ file_mips_check_options ();
+
/* If we are not generating SVR4 PIC code, or if this is NewABI code,
.cprestore is ignored. */
if (mips_pic != SVR4_PIC || HAVE_NEWABI)
return;
}
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cprestore");
+ ignore_rest_of_line ();
+ return;
+ }
+
mips_cprestore_offset = get_absolute_expression ();
mips_cprestore_valid = 1;
ex.X_op_symbol = NULL;
ex.X_add_number = mips_cprestore_offset;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
SP, HAVE_64BIT_ADDRESSES);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
ld $gp, offset($sp)
If a register $reg2 was given there, it results in:
- daddu $gp, $reg2, $0 */
+ or $gp, $reg2, $0 */
static void
s_cpreturn (int ignore ATTRIBUTE_UNUSED)
{
expressionS ex;
+ file_mips_check_options ();
+
/* If we are not generating SVR4 PIC code, .cpreturn is ignored.
We also need NewABI support. */
if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
return;
}
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpreturn");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
if (mips_cpreturn_register == -1)
{
macro_build (&ex, "ld", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP);
}
else
- macro_build (NULL, "daddu", "d,v,t", mips_gp_register,
- mips_cpreturn_register, 0);
+ move_register (mips_gp_register, mips_cpreturn_register);
+
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
-/* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate
- a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for
- use in DWARF debug information. */
+/* Handle a .dtprelword, .dtpreldword, .tprelword, or .tpreldword
+ pseudo-op; DIRSTR says which. The pseudo-op generates a BYTES-size
+ DTP- or TP-relative relocation of type RTYPE, for use in either DWARF
+ debug information or MIPS16 TLS. */
static void
-s_dtprel_internal (size_t bytes)
+s_tls_rel_directive (const size_t bytes, const char *dirstr,
+ bfd_reloc_code_real_type rtype)
{
expressionS ex;
char *p;
if (ex.X_op != O_symbol)
{
- as_bad (_("Unsupported use of %s"), (bytes == 8
- ? ".dtpreldword"
- : ".dtprelword"));
+ as_bad (_("unsupported use of %s"), dirstr);
ignore_rest_of_line ();
}
p = frag_more (bytes);
md_number_to_chars (p, 0, bytes);
- fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
- (bytes == 8
- ? BFD_RELOC_MIPS_TLS_DTPREL64
- : BFD_RELOC_MIPS_TLS_DTPREL32));
-
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, rtype);
demand_empty_rest_of_line ();
+ mips_clear_insn_labels ();
}
/* Handle .dtprelword. */
static void
s_dtprelword (int ignore ATTRIBUTE_UNUSED)
{
- s_dtprel_internal (4);
+ s_tls_rel_directive (4, ".dtprelword", BFD_RELOC_MIPS_TLS_DTPREL32);
}
/* Handle .dtpreldword. */
static void
s_dtpreldword (int ignore ATTRIBUTE_UNUSED)
{
- s_dtprel_internal (8);
+ s_tls_rel_directive (8, ".dtpreldword", BFD_RELOC_MIPS_TLS_DTPREL64);
+}
+
+/* Handle .tprelword. */
+
+static void
+s_tprelword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (4, ".tprelword", BFD_RELOC_MIPS_TLS_TPREL32);
+}
+
+/* Handle .tpreldword. */
+
+static void
+s_tpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (8, ".tpreldword", BFD_RELOC_MIPS_TLS_TPREL64);
}
/* Handle the .gpvalue pseudo-op. This is used when generating NewABI PIC
{
segment_info_type *si;
struct insn_label_list *l;
- symbolS *label;
expressionS ex;
char *p;
si = seg_info (now_seg);
l = si->label_list;
- label = l != NULL ? l->label : NULL;
mips_emit_delays ();
if (auto_align)
- mips_align (2, 0, label);
+ mips_align (2, 0, l);
expression (&ex);
mips_clear_insn_labels ();
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 ();
}
{
segment_info_type *si;
struct insn_label_list *l;
- symbolS *label;
expressionS ex;
char *p;
si = seg_info (now_seg);
l = si->label_list;
- label = l != NULL ? l->label : NULL;
mips_emit_delays ();
if (auto_align)
- mips_align (3, 0, label);
+ mips_align (3, 0, l);
expression (&ex);
mips_clear_insn_labels ();
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 ();
}
demand_empty_rest_of_line ();
}
+/* Handle the .ehword pseudo-op. This is used when generating unwinding
+ tables. It generates a R_MIPS_EH reloc. */
+
+static void
+s_ehword (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ char *p;
+
+ mips_emit_delays ();
+
+ expression (&ex);
+ mips_clear_insn_labels ();
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("unsupported use of .ehword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_32_PCREL);
+
+ demand_empty_rest_of_line ();
+}
+
/* Handle the .cpadd pseudo-op. This is used when dealing with switch
tables in SVR4 PIC code. */
{
int reg;
+ file_mips_check_options ();
+
/* This is ignored when not generating SVR4 PIC code. */
if (mips_pic != SVR4_PIC)
{
return;
}
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
/* Add $gp to the register named as an argument. */
macro_start ();
reg = tc_get_register (0);
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
static void
s_insn (int ignore ATTRIBUTE_UNUSED)
{
+ file_mips_check_options ();
+ file_ase_mips16 |= mips_opts.mips16;
+ file_ase_micromips |= mips_opts.micromips;
+
mips_mark_labels ();
demand_empty_rest_of_line ();
}
-/* Handle a .stabn directive. We need these in order to mark a label
- as being a mips16 text label correctly. Sometimes the compiler
- will emit a label, followed by a .stabn, and then switch sections.
- If the label and .stabn are in mips16 mode, then the label is
- really a mips16 text label. */
+/* Handle the .nan pseudo-op. */
static void
-s_mips_stab (int type)
+s_nan (int ignore ATTRIBUTE_UNUSED)
{
- if (type == 'n')
- mips_mark_labels ();
+ static const char str_legacy[] = "legacy";
+ static const char str_2008[] = "2008";
+ size_t i;
+
+ for (i = 0; !is_end_of_line[(unsigned char) input_line_pointer[i]]; i++);
+
+ if (i == sizeof (str_2008) - 1
+ && memcmp (input_line_pointer, str_2008, i) == 0)
+ mips_nan2008 = 1;
+ else if (i == sizeof (str_legacy) - 1
+ && memcmp (input_line_pointer, str_legacy, i) == 0)
+ {
+ if (ISA_HAS_LEGACY_NAN (file_mips_opts.isa))
+ mips_nan2008 = 0;
+ else
+ as_bad (_("`%s' does not support legacy NaN"),
+ mips_cpu_info_from_isa (file_mips_opts.isa)->name);
+ }
+ else
+ as_bad (_("bad .nan directive"));
+
+ input_line_pointer += i;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .stab[snd] directive. Ideally these directives would be
+ implemented in a transparent way, so that removing them would not
+ have any effect on the generated instructions. However, s_stab
+ internally changes the section, so in practice we need to decide
+ now whether the preceding label marks compressed code. We do not
+ support changing the compression mode of a label after a .stab*
+ directive, such as in:
+
+ foo:
+ .stabs ...
+ .set mips16
+
+ so the current mode wins. */
+static void
+s_mips_stab (int type)
+{
+ mips_mark_labels ();
s_stab (type);
}
symbolS *symbolP;
expressionS exp;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
symbolP = symbol_find_or_make (name);
S_SET_WEAK (symbolP);
*input_line_pointer = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
if (! is_end_of_line[(unsigned char) *input_line_pointer])
{
{
int align = bfd_get_section_alignment (stdoutput, seg);
- if (IS_ELF)
- {
- /* We don't need to align ELF sections to the full alignment.
- However, Irix 5 may prefer that we align them at least to a 16
- byte boundary. We don't bother to align the sections if we
- are targeted for an embedded system. */
- if (strncmp (TARGET_OS, "elf", 3) == 0)
- return addr;
- if (align > 4)
- align = 4;
- }
+ /* We don't need to align ELF sections to the full alignment.
+ However, Irix 5 may prefer that we align them at least to a 16
+ byte boundary. We don't bother to align the sections if we
+ are targeted for an embedded system. */
+ if (strncmp (TARGET_OS, "elf", 3) == 0)
+ return addr;
+ if (align > 4)
+ align = 4;
- return ((addr + (1 << align) - 1) & (-1 << align));
+ return ((addr + (1 << align) - 1) & -(1 << align));
}
/* Utility routine, called from above as well. If called while the
symsec = S_GET_SEGMENT (sym);
/* This must duplicate the test in adjust_reloc_syms. */
- return (symsec != &bfd_und_section
- && symsec != &bfd_abs_section
+ return (!bfd_is_und_section (symsec)
+ && !bfd_is_abs_section (symsec)
&& !bfd_is_com_section (symsec)
&& !s_is_linkonce (sym, segtype)
-#ifdef OBJ_ELF
/* A global or weak symbol is treated as external. */
- && (!IS_ELF || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym)))
-#endif
- );
+ && (!S_IS_WEAK (sym) && !S_IS_EXTERNAL (sym)));
}
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;
if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
return 1;
+ symsec = S_GET_SEGMENT (fragp->fr_symbol);
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);
+ if (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
+ || (operand->root.type == OP_PCREL
+ ? sec != symsec
+ : !bfd_is_abs_section (symsec)))
+ return 1;
sym_frag = symbol_get_frag (fragp->fr_symbol);
- val = S_GET_VALUE (fragp->fr_symbol);
- symsec = S_GET_SEGMENT (fragp->fr_symbol);
+ val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
- 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
- from md_estimate_size_before_relax first. If this is a
- 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. */
- if (sec == NULL)
- {
- if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
- return 1;
- }
- else
- {
- /* Must have been called from md_estimate_size_before_relax. */
- if (symsec != sec)
- {
- fragp->fr_subtype =
- RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
-
- /* FIXME: We should support this, and let the linker
- catch branches and loads that are out of range. */
- as_bad_where (fragp->fr_file, fragp->fr_line,
- _("unsupported PC relative reference to different section"));
+ if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+ return 1;
- return 1;
- }
- if (fragp != sym_frag && sym_frag->fr_address == 0)
- /* Assume non-extended on the first relaxation pass.
- The address we have calculated will be bogus if this is
- a forward branch to another frag, as the forward frag
- will have fr_address == 0. */
- return 0;
- }
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
- /* In this case, we know for sure that the symbol fragment is in
- the same section. If the relax_marker of the symbol fragment
- differs from the relax_marker of this fragment, we have not
- yet adjusted the symbol fragment fr_address. We want to add
- in STRETCH in order to get a better estimate of the address.
- This particularly matters because of the shift bits. */
+ /* If the relax_marker of the symbol fragment differs from the
+ relax_marker of this fragment, we have not yet adjusted the
+ symbol fragment fr_address. We want to add in STRETCH in
+ order to get a better estimate of the address. This
+ particularly matters because of the shift bits. */
if (stretch != 0
&& sym_frag->relax_marker != fragp->relax_marker)
{
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)
+ use an extended opcode. */
+ if ((val & ((1 << operand->shift) - 1)) != 0)
{
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)
- && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
- && sec == NULL)
+ 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))
{
fragp->fr_subtype =
RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
return 1;
}
}
- 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 (fragp
&& S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
{
addressT addr;
toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
}
- else if (fragp)
- /* If the symbol is not defined or it's in a different segment,
- assume the user knows what's going on and emit a short
- branch. */
- toofar = FALSE;
else
+ /* If the symbol is not defined or it's in a different segment,
+ we emit the long sequence. */
toofar = TRUE;
if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
return length;
}
+/* Get a FRAG's branch instruction delay slot size, either from the
+ short-delay-slot bit of a branch-and-link instruction if AL is TRUE,
+ or SHORT_INSN_SIZE otherwise. */
+
+static int
+frag_branch_delay_slot_size (fragS *fragp, bfd_boolean al, int short_insn_size)
+{
+ char *buf = fragp->fr_literal + fragp->fr_fix;
+
+ if (al)
+ return (read_compressed_insn (buf, 4) & 0x02000000) ? 2 : 4;
+ else
+ return short_insn_size;
+}
+
/* Compute the length of a branch sequence, and adjust the
RELAX_MICROMIPS_TOOFAR32 bit accordingly. If FRAGP is NULL, the
worst-case length is computed, with UPDATE being used to indicate
static int
relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
{
+ bfd_boolean insn32 = TRUE;
+ bfd_boolean nods = TRUE;
+ bfd_boolean al = TRUE;
+ int short_insn_size;
bfd_boolean toofar;
int length;
+ if (fragp)
+ {
+ insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+ nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
+ al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
+ }
+ short_insn_size = insn32 ? 4 : 2;
+
if (fragp
&& S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
{
addressT addr;
toofar = val < - (0x8000 << 1) || val >= (0x8000 << 1);
}
- else if (fragp)
- /* If the symbol is not defined or it's in a different segment,
- assume the user knows what's going on and emit a short
- branch. */
- toofar = FALSE;
else
+ /* If the symbol is not defined or it's in a different segment,
+ we emit the long sequence. */
toofar = TRUE;
if (fragp && update
bfd_boolean compact = FALSE;
bfd_boolean uncond;
- if (compact_known)
- compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
if (fragp)
- uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+ {
+ compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+ uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+ }
else
uncond = update < 0;
into:
j label # 4 bytes
- nop # 2 bytes if compact && !PIC
+ nop # 2/4 bytes if
+ # compact && (!PIC || insn32)
0:
*/
- if (mips_pic == NO_PIC && (!compact_known || compact))
- length += 2;
+ if ((mips_pic == NO_PIC || insn32) && (!compact_known || compact))
+ length += short_insn_size;
/* If assembling PIC code, we further turn:
lw/ld at, %got(label)(gp) # 4 bytes
d/addiu at, %lo(label) # 4 bytes
- jr/c at # 2 bytes
+ jr/c at # 2/4 bytes
*/
if (mips_pic != NO_PIC)
- length += 6;
+ length += 4 + short_insn_size;
+
+ /* Add an extra nop if the jump has no compact form and we need
+ to fill the delay slot. */
+ if ((mips_pic == NO_PIC || al) && nods)
+ length += (fragp
+ ? frag_branch_delay_slot_size (fragp, al, short_insn_size)
+ : short_insn_size);
/* If branch <br> is conditional, we prepend negated branch <brneg>:
<brneg> 0f # 4 bytes
- nop # 2 bytes if !compact
+ nop # 2/4 bytes if !compact
*/
if (!uncond)
- length += (compact_known && compact) ? 4 : 6;
+ length += (compact_known && compact) ? 4 : 4 + short_insn_size;
+ }
+ else if (nods)
+ {
+ /* Add an extra nop to fill the delay slot. */
+ gas_assert (fragp);
+ length += frag_branch_delay_slot_size (fragp, al, short_insn_size);
}
return length;
if (fragp
&& S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
{
addressT addr;
if (fixp->fx_addsy == NULL)
return 1;
+ /* Allow relocs used for EH tables. */
+ if (fixp->fx_r_type == BFD_RELOC_32_PCREL)
+ return 1;
+
/* If symbol SYM is in a mergeable section, relocations of the form
SYM + 0 can usually be made section-relative. The mergeable data
is then identified by the section offset rather than by the symbol.
&& (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
return 0;
- /* There is no place to store an in-place offset for JALR relocations.
- Likewise an in-range offset of PC-relative relocations may overflow
- the in-place relocatable field if recalculated against the start
- address of the symbol's containing section. */
- if (HAVE_IN_PLACE_ADDENDS
- && (fixp->fx_pcrel || jalr_reloc_p (fixp->fx_r_type)))
+ /* There is no place to store an in-place offset for JALR relocations. */
+ if (jalr_reloc_p (fixp->fx_r_type) && HAVE_IN_PLACE_ADDENDS)
+ return 0;
+
+ /* Likewise an in-range offset of limited PC-relative relocations may
+ overflow the in-place relocatable field if recalculated against the
+ start address of the symbol's containing section.
+
+ Also, PC relative relocations for MIPS R6 need to be symbol rather than
+ section relative to allow linker relaxations to be performed later on. */
+ if (limited_pcrel_reloc_p (fixp->fx_r_type)
+ && (HAVE_IN_PLACE_ADDENDS || ISA_IS_R6 (file_mips_opts.isa)))
return 0;
-#ifdef OBJ_ELF
/* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
to a floating-point stub. The same is true for non-R_MIPS16_26
relocations against MIPS16 functions; in this case, the stub becomes
There is a further restriction:
5. We cannot reduce jump relocations (R_MIPS_26, R_MIPS16_26 or
- R_MICROMIPS_26_S1) against MIPS16 or microMIPS symbols on
- targets with in-place addends; the relocation field cannot
- encode the low bit.
+ R_MICROMIPS_26_S1) or branch relocations (R_MIPS_PC26_S2,
+ R_MIPS_PC21_S2, R_MIPS_PC16, R_MIPS16_PC16_S1,
+ R_MICROMIPS_PC16_S1, R_MICROMIPS_PC10_S1 or R_MICROMIPS_PC7_S1)
+ against MIPS16 or microMIPS symbols because we need to keep the
+ MIPS16 or microMIPS symbol for the purpose of mode mismatch
+ detection and JAL or BAL to JALX instruction conversion in the
+ linker.
For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
- against a MIPS16 symbol. We deal with (5) by by not reducing any
- such relocations on REL targets.
+ against a MIPS16 symbol. We deal with (5) by additionally leaving
+ alone any jump and branch relocations against a microMIPS symbol.
We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
relocation against some symbol R, no relocation against R may be
targets.) This approach is a little simpler than trying to detect
stub sections, and gives the "all or nothing" per-symbol consistency
that we have for MIPS16 symbols. */
- if (IS_ELF
- && fixp->fx_subsy == NULL
+ if (fixp->fx_subsy == NULL
&& (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
- || *symbol_get_tc (fixp->fx_addsy)
- || (HAVE_IN_PLACE_ADDENDS
- && ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
- && jmp_reloc_p (fixp->fx_r_type))))
+ || (ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
+ && (jmp_reloc_p (fixp->fx_r_type)
+ || b_reloc_p (fixp->fx_r_type)))
+ || *symbol_get_tc (fixp->fx_addsy)))
return 0;
-#endif
return 1;
}
bfd_reloc_code_real_type code;
memset (retval, 0, sizeof(retval));
- reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc = retval[0] = XCNEW (arelent);
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
if (fixp->fx_pcrel)
{
gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS16_16_PCREL_S1
|| 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_MICROMIPS_16_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_32_PCREL
+ || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+ || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+ || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
/* At this point, fx_addnumber is "symbol offset - pcrel address".
Relocations want only the symbol offset. */
- reloc->addend = fixp->fx_addnumber + reloc->address;
- if (!IS_ELF)
+ switch (fixp->fx_r_type)
{
- /* A gruesome hack which is a result of the gruesome gas
- reloc handling. What's worse, for COFF (as opposed to
- ECOFF), we might need yet another copy of reloc->address.
- See bfd_install_relocation. */
- reloc->addend += reloc->address;
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ reloc->addend = fixp->fx_addnumber + (reloc->address & ~7);
+ break;
+ default:
+ reloc->addend = fixp->fx_addnumber + reloc->address;
+ break;
}
}
+ else if (HAVE_IN_PLACE_ADDENDS
+ && fixp->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+ && (read_compressed_insn (fixp->fx_frag->fr_literal
+ + fixp->fx_where, 4) >> 26) == 0x3c)
+ {
+ /* Shift is 2, unusually, for microMIPS JALX. Adjust the in-place
+ addend accordingly. */
+ reloc->addend = fixp->fx_addnumber >> 1;
+ }
else
reloc->addend = fixp->fx_addnumber;
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;
}
if (! RELAX_MIPS16_P (fragp->fr_subtype))
return 0;
- if (mips16_extended_frag (fragp, NULL, stretch))
+ if (mips16_extended_frag (fragp, sec, stretch))
{
if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
return 0;
{
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
- bfd_byte *buf;
+ char *buf;
unsigned long insn;
expressionS exp;
fixS *fixp;
- buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
-
- if (target_big_endian)
- insn = bfd_getb32 (buf);
- else
- insn = bfd_getl32 (buf);
+ buf = fragp->fr_literal + fragp->fr_fix;
+ insn = read_insn (buf);
if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
{
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
- fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, TRUE, BFD_RELOC_16_PCREL_S2);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+ BFD_RELOC_16_PCREL_S2);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
}
else
{
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? and bc1any[24][ft] instructions can
- have the condition reversed by tweaking a single
- bit, and their opcodes all have 0x4???????. */
- gas_assert ((insn & 0xf1000000) == 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:
else
{
/* How many bytes in instructions we've already emitted? */
- i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+ i = buf - fragp->fr_literal - fragp->fr_fix;
/* How many bytes in instructions from here to the end? */
i = fragp->fr_var - i;
}
i--;
insn |= i;
/* Branch over the jump. */
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
/* nop */
- md_number_to_chars ((char *) buf, 0, 4);
- buf += 4;
+ buf = write_insn (buf, 0);
if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
{
/* Compute the PC offset from the current instruction to
the end of the variable frag. */
/* How many bytes in instructions we've already emitted? */
- i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+ i = buf - fragp->fr_literal - fragp->fr_fix;
/* How many bytes in instructions from here to the end? */
i = fragp->fr_var - i;
/* Convert to instruction count. */
i >>= 2;
/* Don't decrement i, because we want to branch over the
delay slot. */
-
insn |= i;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
- md_number_to_chars ((char *) buf, 0, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
+ buf = write_insn (buf, 0);
}
uncond:
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
- fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_MIPS_JMP);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_MIPS_JMP);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
}
else
{
exp.X_add_number = 0;
}
- fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_MIPS_GOT16);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_MIPS_GOT16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
if (mips_opts.isa == ISA_MIPS1)
- {
- /* nop */
- md_number_to_chars ((char *) buf, 0, 4);
- buf += 4;
- }
+ /* nop */
+ buf = write_insn (buf, 0);
/* d/addiu $at, $at, <sym> R_MIPS_LO16 */
insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
insn |= at << OP_SH_RS | at << OP_SH_RT;
- fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_LO16);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_LO16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
/* j(al)r $at. */
if (RELAX_BRANCH_LINK (fragp->fr_subtype))
insn = 0x00000008;
insn |= at << OP_SH_RS;
- md_number_to_chars ((char *) buf, insn, 4);
- buf += 4;
+ buf = write_insn (buf, insn);
}
}
- gas_assert (buf == (bfd_byte *)fragp->fr_literal
- + fragp->fr_fix + fragp->fr_var);
-
fragp->fr_fix += fragp->fr_var;
-
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
return;
}
/* Relax microMIPS branches. */
if (RELAX_MICROMIPS_P (fragp->fr_subtype))
{
- bfd_byte *buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+ char *buf = fragp->fr_literal + fragp->fr_fix;
bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+ bfd_boolean insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+ bfd_boolean nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
bfd_boolean short_ds;
because if there is linker relaxation, we're going to
need the relocations. */
if (type == 'D')
- fixp = fix_new_exp (fragp,
- buf - (bfd_byte *) fragp->fr_literal,
- 2, &exp, TRUE,
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
BFD_RELOC_MICROMIPS_10_PCREL_S1);
else if (type == 'E')
- fixp = fix_new_exp (fragp,
- buf - (bfd_byte *) fragp->fr_literal,
- 2, &exp, TRUE,
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
BFD_RELOC_MICROMIPS_7_PCREL_S1);
else
abort ();
/* We generate a fixup instead of applying it right now,
because if there is linker relaxation, we're going to
need the relocations. */
- fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
- 4, &exp, TRUE, BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+ BFD_RELOC_MICROMIPS_16_PCREL_S1);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
if (type == 0)
- return;
+ {
+ insn = read_compressed_insn (buf, 4);
+ buf += 4;
+
+ if (nods)
+ {
+ /* Check the short-delay-slot bit. */
+ if (!al || (insn & 0x02000000) != 0)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ else
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ }
+
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+ return;
+ }
}
/* Relax 16-bit branches to 32-bit branches. */
if (type != 0)
{
- if (target_big_endian)
- insn = bfd_getb16 (buf);
- else
- insn = bfd_getl16 (buf);
+ insn = read_compressed_insn (buf, 2);
if ((insn & 0xfc00) == 0xcc00) /* b16 */
insn = 0x94000000; /* beq */
if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
|| !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
{
- md_number_to_chars ((char *) buf, insn >> 16, 2);
- buf += 2;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
-
- gas_assert (buf == ((bfd_byte *) fragp->fr_literal
- + fragp->fr_fix));
+ buf = write_compressed_insn (buf, insn, 4);
+ if (nods)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
return;
}
}
else
- {
- unsigned long next;
-
- if (target_big_endian)
- {
- insn = bfd_getb16 (buf);
- next = bfd_getb16 (buf + 2);
- }
- else
- {
- insn = bfd_getl16 (buf);
- next = bfd_getl16 (buf + 2);
- }
- insn = (insn << 16) | next;
- }
+ insn = read_compressed_insn (buf, 4);
/* 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;
+ short_ds = !al || (insn & 0x02000000) != 0;
if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
{
|| (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 ();
/* Make a label at the end for use with the branch. */
l = symbol_new (micromips_label_name (), asec, fragp->fr_fix, fragp);
micromips_label_inc ();
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
- if (IS_ELF)
- S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
-#endif
+ S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
/* Refer to it. */
- fixp = fix_new (fragp, buf - (bfd_byte *) fragp->fr_literal,
- 4, l, 0, TRUE, BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ fixp = fix_new (fragp, buf - fragp->fr_literal, 4, l, 0, TRUE,
+ BFD_RELOC_MICROMIPS_16_PCREL_S1);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
/* Branch over the jump. */
- md_number_to_chars ((char *) buf, insn >> 16, 2);
- buf += 2;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
+ buf = write_compressed_insn (buf, insn, 4);
if (!compact)
{
/* nop */
- insn = 0x0c00;
- md_number_to_chars ((char *) buf, insn, 2);
- buf += 2;
+ if (insn32)
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ else
+ buf = write_compressed_insn (buf, 0x0c00, 2);
}
}
if (mips_pic == NO_PIC)
{
- unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s */
+ unsigned long jal = (short_ds || nods
+ ? 0x74000000 : 0xf4000000); /* jal/s */
/* j/jal/jals <sym> R_MICROMIPS_26_S1 */
insn = al ? jal : 0xd4000000;
- fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_MICROMIPS_JMP);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_JMP);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn >> 16, 2);
- buf += 2;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
+ buf = write_compressed_insn (buf, insn, 4);
- if (compact)
+ if (compact || nods)
{
/* nop */
- insn = 0x0c00;
- md_number_to_chars ((char *) buf, insn, 2);
- buf += 2;
+ if (insn32)
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ else
+ buf = write_compressed_insn (buf, 0x0c00, 2);
}
}
else
{
unsigned long at = RELAX_MICROMIPS_AT (fragp->fr_subtype);
- unsigned long jalr = short_ds ? 0x45e0 : 0x45c0; /* jalr/s */
- unsigned long jr = compact ? 0x45a0 : 0x4580; /* jr/c */
/* lw/ld $at, <sym>($gp) R_MICROMIPS_GOT16 */
insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
exp.X_add_number = 0;
}
- fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_MICROMIPS_GOT16);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_GOT16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn >> 16, 2);
- buf += 2;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
+ buf = write_compressed_insn (buf, insn, 4);
/* d/addiu $at, $at, <sym> R_MICROMIPS_LO16 */
insn = HAVE_64BIT_ADDRESSES ? 0x5c000000 : 0x30000000;
insn |= at << MICROMIPSOP_SH_RT | at << MICROMIPSOP_SH_RS;
- fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
- 4, &exp, FALSE, BFD_RELOC_MICROMIPS_LO16);
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_LO16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
- md_number_to_chars ((char *) buf, insn >> 16, 2);
- buf += 2;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
+ buf = write_compressed_insn (buf, insn, 4);
+
+ if (insn32)
+ {
+ /* jr/jalr $at */
+ insn = 0x00000f3c | (al ? RA : ZERO) << MICROMIPSOP_SH_RT;
+ insn |= at << MICROMIPSOP_SH_RS;
+
+ buf = write_compressed_insn (buf, insn, 4);
+
+ if (compact || nods)
+ /* nop */
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ }
+ else
+ {
+ /* jr/jrc/jalr/jalrs $at */
+ unsigned long jalr = short_ds ? 0x45e0 : 0x45c0; /* jalr/s */
+ unsigned long jr = compact || nods ? 0x45a0 : 0x4580; /* jr/c */
- /* jr/jrc/jalr/jalrs $at */
- insn = al ? jalr : jr;
- insn |= at << MICROMIPSOP_SH_MJ;
+ insn = al ? jalr : jr;
+ insn |= at << MICROMIPSOP_SH_MJ;
- md_number_to_chars ((char *) buf, insn & 0xffff, 2);
- buf += 2;
+ buf = write_compressed_insn (buf, insn, 2);
+ if (al && nods)
+ {
+ /* nop */
+ if (short_ds)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ else
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ }
+ }
}
- gas_assert (buf == (bfd_byte *) fragp->fr_literal + fragp->fr_fix);
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
return;
}
if (RELAX_MIPS16_P (fragp->fr_subtype))
{
int type;
- const struct mips16_immed_operand *op;
- bfd_boolean small, ext;
+ const struct mips_int_operand *operand;
offsetT val;
- bfd_byte *buf;
+ char *buf;
+ unsigned int user_length, length;
+ bfd_boolean need_reloc;
unsigned long insn;
- bfd_boolean use_extend;
- unsigned short extend;
+ bfd_boolean ext;
+ segT symsec;
type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
- op = mips16_immed_operands;
- while (op->type != type)
- ++op;
+ operand = mips16_immed_operand (type, FALSE);
- if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
- {
- small = FALSE;
- ext = TRUE;
- }
- else
- {
- small = TRUE;
- ext = FALSE;
- }
+ ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
+ val = resolve_symbol_value (fragp->fr_symbol) + fragp->fr_offset;
+
+ symsec = S_GET_SEGMENT (fragp->fr_symbol);
+ need_reloc = (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
+ || (operand->root.type == OP_PCREL
+ ? asec != symsec
+ : !bfd_is_abs_section (symsec)));
- 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)
{
+ if (!need_reloc)
+ {
+ if (!ELF_ST_IS_MIPS16 (S_GET_OTHER (fragp->fr_symbol)))
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("branch to a symbol in another ISA mode"));
+ else if ((fragp->fr_offset & 0x1) != 0)
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("branch to misaligned address (0x%lx)"),
+ (long) val);
+ }
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
as_warn_where (fragp->fr_file, fragp->fr_line,
_("extended instruction in delay slot"));
- buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+ buf = fragp->fr_literal + fragp->fr_fix;
- if (target_big_endian)
- insn = bfd_getb16 (buf);
- else
- insn = bfd_getl16 (buf);
+ insn = read_compressed_insn (buf, 2);
+ if (ext)
+ insn |= MIPS16_EXTEND;
- mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
- RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
- small, ext, &insn, &use_extend, &extend);
+ if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+ user_length = 4;
+ else if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+ user_length = 2;
+ else
+ user_length = 0;
- if (use_extend)
+ if (need_reloc)
{
- md_number_to_chars ((char *) buf, 0xf000 | extend, 2);
- fragp->fr_fix += 2;
- buf += 2;
+ bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+ expressionS exp;
+ fixS *fixp;
+
+ switch (type)
+ {
+ case 'p':
+ case 'q':
+ reloc = BFD_RELOC_MIPS16_16_PCREL_S1;
+ break;
+ default:
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("unsupported relocation"));
+ break;
+ }
+ if (reloc == BFD_RELOC_NONE)
+ ;
+ else if (ext)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp,
+ TRUE, reloc);
+
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ /* These relocations can have an addend that won't fit
+ in 2 octets. */
+ fixp->fx_no_overflow = 1;
+ }
+ else
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("invalid unextended operand value"));
}
+ else
+ mips16_immed (fragp->fr_file, fragp->fr_line, type,
+ BFD_RELOC_UNUSED, val, user_length, &insn);
- md_number_to_chars ((char *) buf, insn, 2);
- fragp->fr_fix += 2;
- buf += 2;
+ length = (ext ? 4 : 2);
+ gas_assert (mips16_opcode_length (insn) == length);
+ write_compressed_insn (buf, insn, length);
+ fragp->fr_fix += length;
}
else
{
}
}
-#ifdef OBJ_ELF
-
/* This function is called after the relocs have been generated.
We've been storing mips16 text labels as odd. Here we convert them
back to even for the convenience of the debugger. */
asymbol **syms;
unsigned int count, i;
- if (!IS_ELF)
- return;
-
syms = bfd_get_outsymbols (stdoutput);
count = bfd_get_symcount (stdoutput);
for (i = 0; i < count; i++, syms++)
}
}
-#endif
-
/* This function is called whenever a label is defined, including fake
labels instantiated off the dot special symbol. It is used when
handling branch delays; if a branch has a label, we assume we cannot
move it. This also bumps the value of the symbol by 1 in compressed
code. */
-void
+static void
mips_record_label (symbolS *sym)
{
segment_info_type *si = seg_info (now_seg);
struct insn_label_list *l;
if (free_insn_labels == NULL)
- l = (struct insn_label_list *) xmalloc (sizeof *l);
+ l = XNEW (struct insn_label_list);
else
{
l = free_insn_labels;
mips_define_label (symbolS *sym)
{
mips_record_label (sym);
-#ifdef OBJ_ELF
dwarf2_emit_label (sym);
-#endif
}
-\f
-#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+/* This function is called by tc_new_dot_label whenever a new dot symbol
+ is defined. */
+
+void
+mips_add_dot_label (symbolS *sym)
+{
+ mips_record_label (sym);
+ if (mips_assembling_insn && HAVE_CODE_COMPRESSION)
+ mips_compressed_mark_label (sym);
+}
+\f
+/* Converting ASE flags from internal to .MIPS.abiflags values. */
+static unsigned int
+mips_convert_ase_flags (int ase)
+{
+ unsigned int ext_ases = 0;
+
+ if (ase & ASE_DSP)
+ ext_ases |= AFL_ASE_DSP;
+ if (ase & ASE_DSPR2)
+ ext_ases |= AFL_ASE_DSPR2;
+ if (ase & ASE_DSPR3)
+ ext_ases |= AFL_ASE_DSPR3;
+ if (ase & ASE_EVA)
+ ext_ases |= AFL_ASE_EVA;
+ if (ase & ASE_MCU)
+ ext_ases |= AFL_ASE_MCU;
+ if (ase & ASE_MDMX)
+ ext_ases |= AFL_ASE_MDMX;
+ if (ase & ASE_MIPS3D)
+ ext_ases |= AFL_ASE_MIPS3D;
+ if (ase & ASE_MT)
+ ext_ases |= AFL_ASE_MT;
+ if (ase & ASE_SMARTMIPS)
+ ext_ases |= AFL_ASE_SMARTMIPS;
+ if (ase & ASE_VIRT)
+ ext_ases |= AFL_ASE_VIRT;
+ if (ase & ASE_MSA)
+ ext_ases |= AFL_ASE_MSA;
+ if (ase & ASE_XPA)
+ ext_ases |= AFL_ASE_XPA;
+
+ return ext_ases;
+}
/* Some special processing for a MIPS ELF file. */
void
mips_elf_final_processing (void)
{
+ int fpabi;
+ Elf_Internal_ABIFlags_v0 flags;
+
+ flags.version = 0;
+ flags.isa_rev = 0;
+ switch (file_mips_opts.isa)
+ {
+ case INSN_ISA1:
+ flags.isa_level = 1;
+ break;
+ case INSN_ISA2:
+ flags.isa_level = 2;
+ break;
+ case INSN_ISA3:
+ flags.isa_level = 3;
+ break;
+ case INSN_ISA4:
+ flags.isa_level = 4;
+ break;
+ case INSN_ISA5:
+ flags.isa_level = 5;
+ break;
+ case INSN_ISA32:
+ flags.isa_level = 32;
+ flags.isa_rev = 1;
+ break;
+ case INSN_ISA32R2:
+ flags.isa_level = 32;
+ flags.isa_rev = 2;
+ break;
+ case INSN_ISA32R3:
+ flags.isa_level = 32;
+ flags.isa_rev = 3;
+ break;
+ case INSN_ISA32R5:
+ flags.isa_level = 32;
+ flags.isa_rev = 5;
+ break;
+ case INSN_ISA32R6:
+ flags.isa_level = 32;
+ flags.isa_rev = 6;
+ break;
+ case INSN_ISA64:
+ flags.isa_level = 64;
+ flags.isa_rev = 1;
+ break;
+ case INSN_ISA64R2:
+ flags.isa_level = 64;
+ flags.isa_rev = 2;
+ break;
+ case INSN_ISA64R3:
+ flags.isa_level = 64;
+ flags.isa_rev = 3;
+ break;
+ case INSN_ISA64R5:
+ flags.isa_level = 64;
+ flags.isa_rev = 5;
+ break;
+ case INSN_ISA64R6:
+ flags.isa_level = 64;
+ flags.isa_rev = 6;
+ break;
+ }
+
+ flags.gpr_size = file_mips_opts.gp == 32 ? AFL_REG_32 : AFL_REG_64;
+ flags.cpr1_size = file_mips_opts.soft_float ? AFL_REG_NONE
+ : (file_mips_opts.ase & ASE_MSA) ? AFL_REG_128
+ : (file_mips_opts.fp == 64) ? AFL_REG_64
+ : AFL_REG_32;
+ flags.cpr2_size = AFL_REG_NONE;
+ flags.fp_abi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ flags.isa_ext = bfd_mips_isa_ext (stdoutput);
+ flags.ases = mips_convert_ase_flags (file_mips_opts.ase);
+ if (file_ase_mips16)
+ flags.ases |= AFL_ASE_MIPS16;
+ if (file_ase_micromips)
+ flags.ases |= AFL_ASE_MICROMIPS;
+ flags.flags1 = 0;
+ if ((ISA_HAS_ODD_SINGLE_FPR (file_mips_opts.isa, file_mips_opts.arch)
+ || file_mips_opts.fp == 64)
+ && file_mips_opts.oddspreg)
+ flags.flags1 |= AFL_FLAGS1_ODDSPREG;
+ flags.flags2 = 0;
+
+ bfd_mips_elf_swap_abiflags_v0_out (stdoutput, &flags,
+ ((Elf_External_ABIFlags_v0 *)
+ mips_flags_frag));
+
/* Write out the register information. */
if (mips_abi != N64_ABI)
{
if (mips_abicalls)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
- /* Set MIPS ELF flags for ASEs. */
- /* We may need to define a new flag for DSP ASE, and set this flag when
- file_ase_dsp is true. */
- /* Same for DSP R2. */
- /* We may need to define a new flag for MT ASE, and set this flag when
- file_ase_mt is true. */
+ /* Set MIPS ELF flags for ASEs. Note that not all ASEs have flags
+ defined at present; this might need to change in future. */
if (file_ase_mips16)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
if (file_ase_micromips)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
-#if 0 /* XXX FIXME */
- if (file_ase_mips3d)
- elf_elfheader (stdoutput)->e_flags |= ???;
-#endif
- if (file_ase_mdmx)
+ if (file_mips_opts.ase & ASE_MDMX)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
/* Set the MIPS ELF ABI flags. */
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_32bitmode)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
-#if 0 /* XXX FIXME */
+ if (mips_nan2008 == 1)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008;
+
/* 32 bit code with 64 bit FP registers. */
- if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi))
- elf_elfheader (stdoutput)->e_flags |= ???;
-#endif
+ fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ if (fpabi == Val_GNU_MIPS_ABI_FP_OLD_64)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64;
}
-
-#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
\f
typedef struct proc {
symbolS *func_sym;
*p++ = '\0';
/* Fall through. */
case 2:
- if (nop_opcode == NOP_OPCODE_MICROMIPS)
+ if (nop_opcode == NOP_OPCODE_MICROMIPS && !mips_opts.insn32)
{
- md_number_to_chars (p, micromips_nop16_insn.insn_opcode, 2);
- p += 2;
+ p = write_compressed_insn (p, micromips_nop16_insn.insn_opcode, 2);
break;
}
*p++ = '\0';
fragp->fr_var = size;
}
-static void
-md_obj_begin (void)
-{
-}
-
-static void
-md_obj_end (void)
-{
- /* Check for premature end, nesting errors, etc. */
- if (cur_proc_ptr)
- as_warn (_("missing .end at end of assembly"));
-}
-
static long
get_number (void)
{
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),
else
as_warn (_(".end directive missing or unknown symbol"));
-#ifdef OBJ_ELF
/* Create an expression to calculate the size of the function. */
if (p && cur_proc_ptr)
{
OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p);
- expressionS *exp = xmalloc (sizeof (expressionS));
+ expressionS *exp = XNEW (expressionS);
obj->size = exp;
exp->X_op = O_subtract;
}
/* Generate a .pdr section. */
- if (IS_ELF && !ECOFF_DEBUGGING && mips_flag_pdr)
+ if (!ECOFF_DEBUGGING && mips_flag_pdr)
{
segT saved_seg = now_seg;
subsegT saved_subseg = now_subseg;
subseg_set (saved_seg, saved_subseg);
}
-#endif /* OBJ_ELF */
cur_proc_ptr = NULL;
}
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"));
static void
s_mips_frame (int ignore ATTRIBUTE_UNUSED)
{
-#ifdef OBJ_ELF
- if (IS_ELF && !ECOFF_DEBUGGING)
+ if (ECOFF_DEBUGGING)
+ s_ignore (ignore);
+ else
{
long val;
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;
demand_empty_rest_of_line ();
}
- else
-#endif /* OBJ_ELF */
- s_ignore (ignore);
}
/* The .fmask and .mask directives. If the mdebug section is present
static void
s_mips_mask (int reg_type)
{
-#ifdef OBJ_ELF
- if (IS_ELF && !ECOFF_DEBUGGING)
+ if (ECOFF_DEBUGGING)
+ s_ignore (reg_type);
+ else
{
long mask, off;
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;
demand_empty_rest_of_line ();
}
- else
-#endif /* OBJ_ELF */
- s_ignore (reg_type);
}
/* A table describing all the processors gas knows about. Names are
static const struct mips_cpu_info mips_cpu_info_table[] =
{
/* Entries for generic ISAs */
- { "mips1", MIPS_CPU_IS_ISA, ISA_MIPS1, CPU_R3000 },
- { "mips2", MIPS_CPU_IS_ISA, ISA_MIPS2, CPU_R6000 },
- { "mips3", MIPS_CPU_IS_ISA, ISA_MIPS3, CPU_R4000 },
- { "mips4", MIPS_CPU_IS_ISA, ISA_MIPS4, CPU_R8000 },
- { "mips5", MIPS_CPU_IS_ISA, ISA_MIPS5, CPU_MIPS5 },
- { "mips32", MIPS_CPU_IS_ISA, ISA_MIPS32, CPU_MIPS32 },
- { "mips32r2", MIPS_CPU_IS_ISA, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "mips64", MIPS_CPU_IS_ISA, ISA_MIPS64, CPU_MIPS64 },
- { "mips64r2", MIPS_CPU_IS_ISA, ISA_MIPS64R2, CPU_MIPS64R2 },
+ { "mips1", MIPS_CPU_IS_ISA, 0, ISA_MIPS1, CPU_R3000 },
+ { "mips2", MIPS_CPU_IS_ISA, 0, ISA_MIPS2, CPU_R6000 },
+ { "mips3", MIPS_CPU_IS_ISA, 0, ISA_MIPS3, CPU_R4000 },
+ { "mips4", MIPS_CPU_IS_ISA, 0, ISA_MIPS4, CPU_R8000 },
+ { "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 },
+ { "mips32r6", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R6, CPU_MIPS32R6 },
+ { "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 },
+ { "mips64r6", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R6, CPU_MIPS64R6 },
/* MIPS I */
- { "r3000", 0, ISA_MIPS1, CPU_R3000 },
- { "r2000", 0, ISA_MIPS1, CPU_R3000 },
- { "r3900", 0, ISA_MIPS1, CPU_R3900 },
+ { "r3000", 0, 0, ISA_MIPS1, CPU_R3000 },
+ { "r2000", 0, 0, ISA_MIPS1, CPU_R3000 },
+ { "r3900", 0, 0, ISA_MIPS1, CPU_R3900 },
/* MIPS II */
- { "r6000", 0, ISA_MIPS2, CPU_R6000 },
+ { "r6000", 0, 0, ISA_MIPS2, CPU_R6000 },
/* MIPS III */
- { "r4000", 0, ISA_MIPS3, CPU_R4000 },
- { "r4010", 0, ISA_MIPS2, CPU_R4010 },
- { "vr4100", 0, ISA_MIPS3, CPU_VR4100 },
- { "vr4111", 0, ISA_MIPS3, CPU_R4111 },
- { "vr4120", 0, ISA_MIPS3, CPU_VR4120 },
- { "vr4130", 0, ISA_MIPS3, CPU_VR4120 },
- { "vr4181", 0, ISA_MIPS3, CPU_R4111 },
- { "vr4300", 0, ISA_MIPS3, CPU_R4300 },
- { "r4400", 0, ISA_MIPS3, CPU_R4400 },
- { "r4600", 0, ISA_MIPS3, CPU_R4600 },
- { "orion", 0, ISA_MIPS3, CPU_R4600 },
- { "r4650", 0, ISA_MIPS3, CPU_R4650 },
+ { "r4000", 0, 0, ISA_MIPS3, CPU_R4000 },
+ { "r4010", 0, 0, ISA_MIPS2, CPU_R4010 },
+ { "vr4100", 0, 0, ISA_MIPS3, CPU_VR4100 },
+ { "vr4111", 0, 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4120", 0, 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4130", 0, 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4181", 0, 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4300", 0, 0, ISA_MIPS3, CPU_R4300 },
+ { "r4400", 0, 0, ISA_MIPS3, CPU_R4400 },
+ { "r4600", 0, 0, ISA_MIPS3, CPU_R4600 },
+ { "orion", 0, 0, ISA_MIPS3, CPU_R4600 },
+ { "r4650", 0, 0, ISA_MIPS3, CPU_R4650 },
+ { "r5900", 0, 0, ISA_MIPS3, CPU_R5900 },
/* ST Microelectronics Loongson 2E and 2F cores */
- { "loongson2e", 0, ISA_MIPS3, CPU_LOONGSON_2E },
- { "loongson2f", 0, ISA_MIPS3, CPU_LOONGSON_2F },
+ { "loongson2e", 0, 0, ISA_MIPS3, CPU_LOONGSON_2E },
+ { "loongson2f", 0, 0, ISA_MIPS3, CPU_LOONGSON_2F },
/* MIPS IV */
- { "r8000", 0, ISA_MIPS4, CPU_R8000 },
- { "r10000", 0, ISA_MIPS4, CPU_R10000 },
- { "r12000", 0, ISA_MIPS4, CPU_R12000 },
- { "r14000", 0, ISA_MIPS4, CPU_R14000 },
- { "r16000", 0, ISA_MIPS4, CPU_R16000 },
- { "vr5000", 0, ISA_MIPS4, CPU_R5000 },
- { "vr5400", 0, ISA_MIPS4, CPU_VR5400 },
- { "vr5500", 0, ISA_MIPS4, CPU_VR5500 },
- { "rm5200", 0, ISA_MIPS4, CPU_R5000 },
- { "rm5230", 0, ISA_MIPS4, CPU_R5000 },
- { "rm5231", 0, ISA_MIPS4, CPU_R5000 },
- { "rm5261", 0, ISA_MIPS4, CPU_R5000 },
- { "rm5721", 0, ISA_MIPS4, CPU_R5000 },
- { "rm7000", 0, ISA_MIPS4, CPU_RM7000 },
- { "rm9000", 0, ISA_MIPS4, CPU_RM9000 },
+ { "r8000", 0, 0, ISA_MIPS4, CPU_R8000 },
+ { "r10000", 0, 0, ISA_MIPS4, CPU_R10000 },
+ { "r12000", 0, 0, ISA_MIPS4, CPU_R12000 },
+ { "r14000", 0, 0, ISA_MIPS4, CPU_R14000 },
+ { "r16000", 0, 0, ISA_MIPS4, CPU_R16000 },
+ { "vr5000", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "vr5400", 0, 0, ISA_MIPS4, CPU_VR5400 },
+ { "vr5500", 0, 0, ISA_MIPS4, CPU_VR5500 },
+ { "rm5200", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5230", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5231", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5261", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5721", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm7000", 0, 0, ISA_MIPS4, CPU_RM7000 },
+ { "rm9000", 0, 0, ISA_MIPS4, CPU_RM9000 },
/* MIPS 32 */
- { "4kc", 0, ISA_MIPS32, CPU_MIPS32 },
- { "4km", 0, ISA_MIPS32, CPU_MIPS32 },
- { "4kp", 0, ISA_MIPS32, CPU_MIPS32 },
- { "4ksc", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32, CPU_MIPS32 },
+ { "4kc", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4km", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4kp", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4ksc", 0, ASE_SMARTMIPS, ISA_MIPS32, CPU_MIPS32 },
/* MIPS 32 Release 2 */
- { "4kec", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "4kem", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "4kep", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "4ksd", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m4kp", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m14k", MIPS_CPU_ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m14kc", MIPS_CPU_ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m14ke", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2 | MIPS_CPU_ASE_MCU,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "m14kec", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2 | MIPS_CPU_ASE_MCU,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kf2_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kf1_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4kec", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4kem", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4kep", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4ksd", 0, ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m4k", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m4kp", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14k", 0, ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14kc", 0, ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14ke", 0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+ ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14kec", 0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+ ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kc", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf2_1", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf1_1", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
/* Deprecated forms of the above. */
- { "24kfx", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kx", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kfx", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kx", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
/* 24KE is a 24K with DSP ASE, other ASEs are optional. */
- { "24kec", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kef2_1", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kef", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kef1_1", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kec", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef2_1", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef1_1", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
/* Deprecated forms of the above. */
- { "24kefx", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
- { "24kex", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kefx", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kex", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
/* 34K is a 24K with DSP and MT ASE, other ASEs are optional. */
- { "34kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "34kf2_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "34kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "34kf1_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kc", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf2_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf1_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
/* Deprecated forms of the above. */
- { "34kfx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "34kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kfx", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kx", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 34Kn is a 34kc without DSP. */
+ { "34kn", 0, ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
/* 74K with DSP and DSPR2 ASE, other ASEs are optional. */
- { "74kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "74kf2_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "74kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "74kf1_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "74kf3_2", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kc", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf2_1", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf1_1", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf3_2", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
/* Deprecated forms of the above. */
- { "74kfx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "74kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
- ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kfx", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kx", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
/* 1004K cores are multiprocessor versions of the 34K. */
- { "1004kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "1004kf2_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "1004kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
- { "1004kf1_1", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
- ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "1004kc", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "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 },
+ /* interaptiv is the new name for 1004kf */
+ { "interaptiv", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* M5100 family */
+ { "m5100", 0, ASE_MCU, ISA_MIPS32R5, CPU_MIPS32R5 },
+ { "m5101", 0, ASE_MCU, ISA_MIPS32R5, CPU_MIPS32R5 },
+ /* 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, ISA_MIPS64, CPU_MIPS64 },
- { "5kf", 0, ISA_MIPS64, CPU_MIPS64 },
- { "20kc", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
- { "25kf", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
+ { "5kc", 0, 0, ISA_MIPS64, CPU_MIPS64 },
+ { "5kf", 0, 0, ISA_MIPS64, CPU_MIPS64 },
+ { "20kc", 0, ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
+ { "25kf", 0, ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
/* Broadcom SB-1 CPU core */
- { "sb1", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX,
- ISA_MIPS64, CPU_SB1 },
+ { "sb1", 0, ASE_MIPS3D | ASE_MDMX, ISA_MIPS64, CPU_SB1 },
/* Broadcom SB-1A CPU core */
- { "sb1a", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX,
- ISA_MIPS64, CPU_SB1 },
-
- { "loongson3a", 0, ISA_MIPS64, CPU_LOONGSON_3A },
+ { "sb1a", 0, ASE_MIPS3D | ASE_MDMX, ISA_MIPS64, CPU_SB1 },
+
+ { "loongson3a", 0, 0, ISA_MIPS64R2, CPU_LOONGSON_3A },
/* MIPS 64 Release 2 */
/* Cavium Networks Octeon CPU core */
- { "octeon", 0, ISA_MIPS64R2, CPU_OCTEON },
+ { "octeon", 0, 0, ISA_MIPS64R2, CPU_OCTEON },
+ { "octeon+", 0, 0, ISA_MIPS64R2, CPU_OCTEONP },
+ { "octeon2", 0, 0, ISA_MIPS64R2, CPU_OCTEON2 },
+ { "octeon3", 0, ASE_VIRT | ASE_VIRT64, ISA_MIPS64R5, CPU_OCTEON3 },
/* RMI Xlr */
- { "xlr", 0, ISA_MIPS64, CPU_XLR },
+ { "xlr", 0, 0, ISA_MIPS64, CPU_XLR },
+
+ /* Broadcom XLP.
+ XLP is mostly like XLR, with the prominent exception that it is
+ MIPS64R2 rather than MIPS64. */
+ { "xlp", 0, 0, ISA_MIPS64R2, CPU_XLR },
+
+ /* MIPS 64 Release 6 */
+ { "i6400", 0, ASE_MSA, ISA_MIPS64R6, CPU_MIPS64R6},
+ { "p6600", 0, ASE_VIRT | ASE_MSA, ISA_MIPS64R6, CPU_MIPS64R6},
/* End marker */
- { NULL, 0, 0, 0 }
+ { NULL, 0, 0, 0, 0 }
};
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\
+-mips32r6 generate MIPS32 release 6 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\
+-mips64r6 generate MIPS64 release 6 ISA instructions\n\
-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n"));
first = 1;
-mno-micromips do not generate microMIPS instructions\n"));
fprintf (stream, _("\
-msmartmips generate smartmips instructions\n\
--mno-smartmips do not generate smartmips instructions\n"));
+-mno-smartmips do not generate smartmips instructions\n"));
fprintf (stream, _("\
-mdsp generate DSP instructions\n\
-mno-dsp do not generate DSP instructions\n"));
-mdspr2 generate DSP R2 instructions\n\
-mno-dspr2 do not generate DSP R2 instructions\n"));
fprintf (stream, _("\
+-mdspr3 generate DSP R3 instructions\n\
+-mno-dspr3 do not generate DSP R3 instructions\n"));
+ fprintf (stream, _("\
-mmt generate MT instructions\n\
-mno-mt do not generate MT instructions\n"));
fprintf (stream, _("\
-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, _("\
+-minsn32 only generate 32-bit microMIPS instructions\n\
+-mno-insn32 generate all microMIPS instructions\n"));
+ fprintf (stream, _("\
-mfix-loongson2f-jump work around Loongson2F JUMP instructions\n\
-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
-mfix-vr4120 work around certain VR4120 errata\n\
-msoft-float do not allow floating-point instructions\n\
-msingle-float only allow 32-bit floating-point operations\n\
-mdouble-float allow 32-bit and 64-bit floating-point operations\n\
---[no-]construct-floats [dis]allow floating point values to be constructed\n"
- ));
-#ifdef OBJ_ELF
+--[no-]construct-floats [dis]allow floating point values to be constructed\n\
+--[no-]relax-branch [dis]allow out-of-range branches to be relaxed\n\
+-mnan=ENCODING select an IEEE 754 NaN encoding convention, either of:\n"));
+
+ first = 1;
+
+ show (stream, "legacy", &column, &first);
+ show (stream, "2008", &column, &first);
+
+ fputc ('\n', stream);
+
fprintf (stream, _("\
-KPIC, -call_shared generate SVR4 position independent code\n\
-call_nonpic generate non-PIC code that can operate with DSOs\n\
-32 create o32 ABI object file (default)\n\
-n32 create n32 ABI object file\n\
-64 create 64 ABI object file\n"));
-#endif
}
#ifdef TE_IRIX
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;
+}
+
+void
+md_mips_end (void)
+{
+ int fpabi = Val_GNU_MIPS_ABI_FP_ANY;
+
+ mips_emit_delays ();
+ if (cur_proc_ptr)
+ as_warn (_("missing .end at end of assembly"));
+
+ /* Just in case no code was emitted, do the consistency check. */
+ file_mips_check_options ();
+
+ /* Set a floating-point ABI if the user did not. */
+ if (obj_elf_seen_attribute (OBJ_ATTR_GNU, Tag_GNU_MIPS_ABI_FP))
+ {
+ /* Perform consistency checks on the floating-point ABI. */
+ fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ if (fpabi != Val_GNU_MIPS_ABI_FP_ANY)
+ check_fpabi (fpabi);
+ }
+ else
+ {
+ /* Soft-float gets precedence over single-float, the two options should
+ not be used together so this should not matter. */
+ if (file_mips_opts.soft_float == 1)
+ fpabi = Val_GNU_MIPS_ABI_FP_SOFT;
+ /* Single-float gets precedence over all double_float cases. */
+ else if (file_mips_opts.single_float == 1)
+ fpabi = Val_GNU_MIPS_ABI_FP_SINGLE;
+ else
+ {
+ switch (file_mips_opts.fp)
+ {
+ case 32:
+ if (file_mips_opts.gp == 32)
+ fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+ break;
+ case 0:
+ fpabi = Val_GNU_MIPS_ABI_FP_XX;
+ break;
+ case 64:
+ if (file_mips_opts.gp == 32 && !file_mips_opts.oddspreg)
+ fpabi = Val_GNU_MIPS_ABI_FP_64A;
+ else if (file_mips_opts.gp == 32)
+ fpabi = Val_GNU_MIPS_ABI_FP_64;
+ else
+ fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+ break;
+ }
+ }
+
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP, fpabi);
+ }
+}
+
+/* Returns the relocation type required for a particular CFI encoding. */
+
+bfd_reloc_code_real_type
+mips_cfi_reloc_for_encoding (int encoding)
+{
+ if (encoding == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
+ return BFD_RELOC_32_PCREL;
+ else return BFD_RELOC_NONE;
+}