| /* tc-mips.c -- assemble code for a MIPS chip. |
| Copyright (C) 1993-2021 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 |
| Support. |
| |
| This file is part of GAS. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GAS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| #include "as.h" |
| #include "config.h" |
| #include "subsegs.h" |
| #include "safe-ctype.h" |
| |
| #include "opcode/mips.h" |
| #include "itbl-ops.h" |
| #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 |
| |
| #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; } |
| #undef OBJ_PROCESS_STAB |
| #undef OUTPUT_FLAVOR |
| #undef S_GET_ALIGN |
| #undef S_GET_SIZE |
| #undef S_SET_ALIGN |
| #undef S_SET_SIZE |
| #undef obj_frob_file |
| #undef obj_frob_file_after_relocs |
| #undef obj_frob_symbol |
| #undef obj_pop_insert |
| #undef obj_sec_sym_ok_for_reloc |
| #undef OBJ_COPY_SYMBOL_ATTRIBUTES |
| |
| #include "obj-elf.h" |
| /* Fix any of them that we actually care about. */ |
| #undef OUTPUT_FLAVOR |
| #define OUTPUT_FLAVOR mips_output_flavor() |
| |
| #include "elf/mips.h" |
| |
| #ifndef ECOFF_DEBUGGING |
| #define NO_ECOFF_DEBUGGING |
| #define ECOFF_DEBUGGING 0 |
| #endif |
| |
| int mips_flag_mdebug = -1; |
| |
| /* Control generation of .pdr sections. Off by default on IRIX: the native |
| linker doesn't know about and discards them, but relocations against them |
| remain, leading to rld crashes. */ |
| #ifdef TE_IRIX |
| int mips_flag_pdr = false; |
| #else |
| int mips_flag_pdr = true; |
| #endif |
| |
| #include "ecoff.h" |
| |
| static char *mips_regmask_frag; |
| static char *mips_flags_frag; |
| |
| #define ZERO 0 |
| #define ATREG 1 |
| #define S0 16 |
| #define S7 23 |
| #define TREG 24 |
| #define PIC_CALL_REG 25 |
| #define KT0 26 |
| #define KT1 27 |
| #define GP 28 |
| #define SP 29 |
| #define FP 30 |
| #define RA 31 |
| |
| #define FCSR 31 |
| |
| #define ILLEGAL_REG (32) |
| |
| #define AT mips_opts.at |
| |
| extern int target_big_endian; |
| |
| /* The name of the readonly data section. */ |
| #define RDATA_SECTION_NAME ".rodata" |
| |
| /* Ways in which an instruction can be "appended" to the output. */ |
| enum append_method { |
| /* Just add it normally. */ |
| APPEND_ADD, |
| |
| /* Add it normally and then add a nop. */ |
| APPEND_ADD_WITH_NOP, |
| |
| /* Turn an instruction with a delay slot into a "compact" version. */ |
| APPEND_ADD_COMPACT, |
| |
| /* Insert the instruction before the last one. */ |
| APPEND_SWAP |
| }; |
| |
| /* Information about an instruction, including its format, operands |
| and fixups. */ |
| struct mips_cl_insn |
| { |
| /* The opcode's entry in mips_opcodes or mips16_opcodes. */ |
| const struct mips_opcode *insn_mo; |
| |
| /* The 16-bit or 32-bit bitstring of the instruction itself. This is |
| 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 name if this is an label. */ |
| char label[16]; |
| |
| /* The target label name if this is an branch. */ |
| char target[16]; |
| |
| /* The frag that contains the instruction. */ |
| struct frag *frag; |
| |
| /* The offset into FRAG of the first instruction byte. */ |
| long where; |
| |
| /* The relocs associated with the instruction, if any. */ |
| fixS *fixp[3]; |
| |
| /* True if this entry cannot be moved from its current position. */ |
| unsigned int fixed_p : 1; |
| |
| /* True if this instruction occurred in a .set noreorder block. */ |
| unsigned int noreorder_p : 1; |
| |
| /* True for mips16 instructions that jump to an absolute address. */ |
| unsigned int mips16_absolute_jump_p : 1; |
| |
| /* 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. */ |
| enum mips_abi_level |
| { |
| NO_ABI = 0, |
| O32_ABI, |
| O64_ABI, |
| N32_ABI, |
| N64_ABI, |
| EABI_ABI |
| }; |
| |
| /* MIPS ABI we are using for this output file. */ |
| static enum mips_abi_level mips_abi = NO_ABI; |
| |
| /* Whether or not we have code that can call pic code. */ |
| int mips_abicalls = false; |
| |
| /* Whether or not we have code which can be put into a shared |
| library. */ |
| static bool mips_in_shared = true; |
| |
| /* This is the set of options which may be modified by the .set |
| pseudo-op. We use a struct so that .set push and .set pop are more |
| reliable. */ |
| |
| struct mips_set_options |
| { |
| /* MIPS ISA (Instruction Set Architecture) level. This is set to -1 |
| 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). 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 |
| -nomips16 command line options, and the default CPU. */ |
| int mips16; |
| /* Whether we are assembling for the mipsMIPS ASE. 0 if we are not, |
| 1 if we are, and -1 if the value has not been initialized. Changed |
| by `.set micromips' and `.set nomicromips', and the -mmicromips |
| and -mno-micromips command line options, and the default CPU. */ |
| int micromips; |
| /* Non-zero if we should not reorder instructions. Changed by `.set |
| reorder' and `.set noreorder'. */ |
| int noreorder; |
| /* Non-zero if we should not permit the register designated "assembler |
| temporary" to be used in instructions. The value is the register |
| number, normally $at ($1). Changed by `.set at=REG', `.set noat' |
| (same as `.set at=$0') and `.set at' (same as `.set at=$1'). */ |
| unsigned int at; |
| /* Non-zero if we should warn when a macro instruction expands into |
| more than one machine instruction. Changed by `.set nomacro' and |
| `.set macro'. */ |
| int warn_about_macros; |
| /* Non-zero if we should not move instructions. Changed by `.set |
| move', `.set volatile', `.set nomove', and `.set novolatile'. */ |
| int nomove; |
| /* Non-zero if we should not optimize branches by moving the target |
| of the branch into the delay slot. Actually, we don't perform |
| this optimization anyhow. Changed by `.set bopt' and `.set |
| nobopt'. */ |
| int nobopt; |
| /* 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. */ |
| bool 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 gp; |
| int fp; |
| /* MIPS architecture (CPU) type. Changed by .set arch=FOO, the -march |
| command line option, and the default CPU. */ |
| int arch; |
| /* True if ".set sym32" is in effect. */ |
| bool sym32; |
| /* True if floating-point operations are not allowed. Changed by .set |
| softfloat or .set hardfloat, by command line options -msoft-float or |
| -mhard-float. The default is false. */ |
| bool soft_float; |
| |
| /* True if only single-precision floating-point operations are allowed. |
| Changed by .set singlefloat or .set doublefloat, command-line options |
| -msingle-float or -mdouble-float. The default is false. */ |
| bool single_float; |
| |
| /* 1 if single-precision operations on odd-numbered registers are |
| allowed. */ |
| int oddspreg; |
| |
| /* The set of ASEs that should be enabled for the user specified |
| architecture. This cannot be inferred from 'arch' for all cores |
| as processors only have a unique 'arch' if they add architecture |
| specific instructions (UDI). */ |
| int init_ase; |
| }; |
| |
| /* Specifies whether module level options have been checked yet. */ |
| static bool file_mips_opts_checked = false; |
| |
| /* 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; |
| |
| /* 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. */ |
| |
| 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, |
| /* init_ase */ 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 */ 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, |
| /* init_ase */ 0 |
| }; |
| |
| /* 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]; |
| |
| /* 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_MIPS64R3 \ |
| || mips_opts.isa == ISA_MIPS64R5) |
| |
| /* True if any microMIPS code was produced. */ |
| static int file_ase_micromips; |
| |
| /* True if we want to create R_MIPS_JALR for jalr $25. */ |
| #ifdef TE_IRIX |
| #define MIPS_JALR_HINT_P(EXPR) HAVE_NEWABI |
| #else |
| /* As a GNU extension, we use R_MIPS_JALR for o32 too. However, |
| because there's no place for any addend, the only acceptable |
| expression is a bare symbol. */ |
| #define MIPS_JALR_HINT_P(EXPR) \ |
| (!HAVE_IN_PLACE_ADDENDS \ |
| || ((EXPR)->X_op == O_symbol && (EXPR)->X_add_number == 0)) |
| #endif |
| |
| /* The argument of the -march= flag. The architecture we are assembling. */ |
| static const char *mips_arch_string; |
| |
| /* The argument of the -mtune= flag. The architecture for which we |
| are optimizing. */ |
| static int mips_tune = CPU_UNKNOWN; |
| static const char *mips_tune_string; |
| |
| /* True when generating 32-bit code for a 64-bit processor. */ |
| static int mips_32bitmode = 0; |
| |
| /* True if the given ABI requires 32-bit registers. */ |
| #define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI) |
| |
| /* Likewise 64-bit registers. */ |
| #define ABI_NEEDS_64BIT_REGS(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_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_MIPS3 \ |
| || (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_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)) \ |
| ) |
| |
| /* Return true if ISA supports 32-bit right rotate (ror et al.) |
| instructions. */ |
| #define ISA_HAS_ROR(ISA) \ |
| ((ISA) == ISA_MIPS32R2 \ |
| || (ISA) == ISA_MIPS32R3 \ |
| || (ISA) == ISA_MIPS32R5 \ |
| || (ISA) == ISA_MIPS32R6 \ |
| || (ISA) == ISA_MIPS64R2 \ |
| || (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, 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_GS464 \ |
| || (CPU) != CPU_GS464E \ |
| || (CPU) != CPU_GS264E)) |
| |
| /* 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_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 GPR_SIZE \ |
| (mips_opts.gp == 64 && !ISA_HAS_64BIT_REGS (mips_opts.isa) \ |
| ? 32 \ |
| : mips_opts.gp) |
| |
| #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) |
| |
| #define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI) |
| |
| /* True if relocations are stored in-place. */ |
| #define HAVE_IN_PLACE_ADDENDS (!HAVE_NEWABI) |
| |
| /* The ABI-derived address size. */ |
| #define HAVE_64BIT_ADDRESSES \ |
| (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 |
| "SYMBOL" or "SYMBOL + OFFSET"). */ |
| #define HAVE_32BIT_SYMBOLS \ |
| (HAVE_32BIT_ADDRESSES || !HAVE_64BIT_OBJECTS || mips_opts.sym32) |
| #define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS) |
| |
| /* Addresses are loaded in different ways, depending on the address size |
| in use. The n32 ABI Documentation also mandates the use of additions |
| with overflow checking, but existing implementations don't follow it. */ |
| #define ADDRESS_ADD_INSN \ |
| (HAVE_32BIT_ADDRESSES ? "addu" : "daddu") |
| |
| #define ADDRESS_ADDI_INSN \ |
| (HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu") |
| |
| #define ADDRESS_LOAD_INSN \ |
| (HAVE_32BIT_ADDRESSES ? "lw" : "ld") |
| |
| #define ADDRESS_STORE_INSN \ |
| (HAVE_32BIT_ADDRESSES ? "sw" : "sd") |
| |
| /* Return true if the given CPU supports the MIPS16 ASE. */ |
| #define CPU_HAS_MIPS16(cpu) \ |
| (startswith (TARGET_CPU, "mips16") \ |
| || startswith (TARGET_CANONICAL, "mips-lsi-elf")) |
| |
| /* Return true if the given CPU supports the microMIPS ASE. */ |
| #define CPU_HAS_MICROMIPS(cpu) 0 |
| |
| /* True if CPU has a dror instruction. */ |
| #define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500) |
| |
| /* 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_IS_OCTEON (CPU)) |
| |
| /* 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. |
| |
| According to MIPS specifications, MIPS ISAs I, II, and III need |
| (at least) two instructions between the reads of HI/LO and |
| instructions which write them, and later ISAs do not. Contradicting |
| the MIPS specifications, some MIPS IV processor user manuals (e.g. |
| the UM for the NEC Vr5000) document needing the instructions between |
| HI/LO reads and writes, as well. Therefore, we declare only MIPS32, |
| MIPS64 and later ISAs to have the interlocks, plus any specific |
| earlier-ISA CPUs for which CPU documentation declares that the |
| instructions are really interlocked. */ |
| #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 \ |
| || mips_opts.arch == CPU_R16000 \ |
| || mips_opts.arch == CPU_RM7000 \ |
| || mips_opts.arch == CPU_VR5500 \ |
| || mips_opts.micromips \ |
| ) |
| |
| /* 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. 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, 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. */ |
| /* Itbl support may require additional care here. */ |
| #define cop_interlocks \ |
| ((mips_opts.isa != ISA_MIPS1 \ |
| && mips_opts.isa != ISA_MIPS2 \ |
| && mips_opts.isa != ISA_MIPS3) \ |
| || mips_opts.arch == CPU_R4300 \ |
| || mips_opts.micromips \ |
| ) |
| |
| /* Whether the processor uses hardware interlocks to protect reads |
| from coprocessor registers after they are loaded from memory, and |
| thus does not require nops to be inserted. This applies to |
| instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only |
| requires at MIPS ISA level I and microMIPS mode instructions are |
| always interlocked. */ |
| #define cop_mem_interlocks \ |
| (mips_opts.isa != ISA_MIPS1 \ |
| || mips_opts.micromips \ |
| ) |
| |
| /* Is this a mfhi or mflo instruction? */ |
| #define MF_HILO_INSN(PINFO) \ |
| ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO)) |
| |
| /* 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; |
| |
| /* 1 if we should generate 32 bit offsets from the $gp register in |
| SVR4_PIC mode. Currently has no meaning in other modes. */ |
| static int mips_big_got = 0; |
| |
| /* 1 if trap instructions should used for overflow rather than break |
| instructions. */ |
| static int mips_trap = 0; |
| |
| /* 1 if double width floating point constants should not be constructed |
| by assembling two single width halves into two single width floating |
| point registers which just happen to alias the double width destination |
| register. On some architectures this aliasing can be disabled by a bit |
| in the status register, and the setting of this bit cannot be determined |
| automatically at assemble time. */ |
| static int mips_disable_float_construction; |
| |
| /* Non-zero if any .set noreorder directives were used. */ |
| |
| static int mips_any_noreorder; |
| |
| /* Non-zero if nops should be inserted when the register referenced in |
| an mfhi/mflo instruction is read in the next two instructions. */ |
| static int mips_7000_hilo_fix; |
| |
| /* The size of objects in the small data section. */ |
| static unsigned int g_switch_value = 8; |
| /* Whether the -G option was used. */ |
| static int g_switch_seen = 0; |
| |
| #define N_RMASK 0xc4 |
| #define N_VFP 0xd4 |
| |
| /* If we can determine in advance that GP optimization won't be |
| possible, we can skip the relaxation stuff that tries to produce |
| GP-relative references. This makes delay slot optimization work |
| better. |
| |
| This function can only provide a guess, but it seems to work for |
| gcc output. It needs to guess right for gcc, otherwise gcc |
| will put what it thinks is a GP-relative instruction in a branch |
| delay slot. |
| |
| I don't know if a fix is needed for the SVR4_PIC mode. I've only |
| fixed it for the non-PIC mode. KR 95/04/07 */ |
| static int nopic_need_relax (symbolS *, int); |
| |
| /* Handle of the OPCODE hash table. */ |
| static htab_t op_hash = NULL; |
| |
| /* The opcode hash table we use for the mips16. */ |
| static htab_t mips16_op_hash = NULL; |
| |
| /* The opcode hash table we use for the microMIPS ASE. */ |
| static htab_t micromips_op_hash = NULL; |
| |
| /* This array holds the chars that always start a comment. If the |
| pre-processor is disabled, these aren't very useful. */ |
| const char comment_chars[] = "#"; |
| |
| /* This array holds the chars that only start a comment at the beginning of |
| a line. If the line seems to have the form '# 123 filename' |
| .line and .file directives will appear in the pre-processed output. */ |
| /* Note that input_file.c hand checks for '#' at the beginning of the |
| first line of the input file. This is because the compiler outputs |
| #NO_APP at the beginning of its output. */ |
| /* Also note that C style comments are always supported. */ |
| const char line_comment_chars[] = "#"; |
| |
| /* This array holds machine specific line separator characters. */ |
| const char line_separator_chars[] = ";"; |
| |
| /* Chars that can be used to separate mant from exp in floating point nums. */ |
| const char EXP_CHARS[] = "eE"; |
| |
| /* Chars that mean this number is a floating point constant. |
| As in 0f12.456 |
| or 0d1.2345e12. */ |
| const char FLT_CHARS[] = "rRsSfFdDxXpP"; |
| |
| /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be |
| changed in read.c . Ideally it shouldn't have to know about it at all, |
| but nothing is ideal around here. */ |
| |
| /* 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; |
| |
| /* When outputting SVR4 PIC code, the assembler needs to know the |
| offset in the stack frame from which to restore the $gp register. |
| This is set by the .cprestore pseudo-op, and saved in this |
| variable. */ |
| static offsetT mips_cprestore_offset = -1; |
| |
| /* Similar for NewABI PIC code, where $gp is callee-saved. NewABI has some |
| more optimizations, it can use a register value instead of a memory-saved |
| offset and even an other register than $gp as global pointer. */ |
| static offsetT mips_cpreturn_offset = -1; |
| static int mips_cpreturn_register = -1; |
| static int mips_gp_register = GP; |
| static int mips_gprel_offset = 0; |
| |
| /* Whether mips_cprestore_offset has been set in the current function |
| (or whether it has already been warned about, if not). */ |
| static int mips_cprestore_valid = 0; |
| |
| /* This is the register which holds the stack frame, as set by the |
| .frame pseudo-op. This is needed to implement .cprestore. */ |
| static int mips_frame_reg = SP; |
| |
| /* Whether mips_frame_reg has been set in the current function |
| (or whether it has already been warned about, if not). */ |
| static int mips_frame_reg_valid = 0; |
| |
| /* To output NOP instructions correctly, we need to keep information |
| about the previous two instructions. */ |
| |
| /* Whether we are optimizing. The default value of 2 means to remove |
| unneeded NOPs and swap branch instructions when possible. A value |
| of 1 means to not swap branches. A value of 0 means to always |
| insert NOPs. */ |
| static int mips_optimize = 2; |
| |
| /* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is |
| equivalent to seeing no -g option at all. */ |
| static int mips_debug = 0; |
| |
| /* The maximum number of NOPs needed to avoid the VR4130 mflo/mfhi errata. */ |
| #define MAX_VR4130_NOPS 4 |
| |
| /* The maximum number of NOPs needed to fill delay slots. */ |
| #define MAX_DELAY_NOPS 2 |
| |
| /* The maximum number of NOPs needed for any purpose. */ |
| #define MAX_NOPS 4 |
| |
| /* The maximum range of context length of ll/sc. */ |
| #define MAX_LLSC_RANGE 20 |
| |
| /* A list of previous instructions, with index 0 being the most recent. |
| We need to look back MAX_NOPS instructions when filling delay slots |
| or working around processor errata. We need to look back one |
| instruction further if we're thinking about using history[0] to |
| fill a branch delay slot. */ |
| static struct mips_cl_insn history[1 + MAX_NOPS + MAX_LLSC_RANGE]; |
| |
| /* The maximum number of LABELS detect for the same address. */ |
| #define MAX_LABELS_SAME 10 |
| |
| /* 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_nop16_insn; |
| static struct mips_cl_insn micromips_nop32_insn; |
| |
| /* Sync instructions used by insert sync. */ |
| static struct mips_cl_insn sync_insn; |
| |
| /* The appropriate nop for the current mode. */ |
| #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 ((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 |
| nops turn out to be unnecessary, the size of the frag can be |
| decreased. */ |
| static fragS *prev_nop_frag; |
| |
| /* The number of nop instructions we created in prev_nop_frag. */ |
| static int prev_nop_frag_holds; |
| |
| /* The number of nop instructions that we know we need in |
| prev_nop_frag. */ |
| static int prev_nop_frag_required; |
| |
| /* The number of instructions we've seen since prev_nop_frag. */ |
| static int prev_nop_frag_since; |
| |
| /* 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 |
| corresponding LO relocation. */ |
| |
| struct mips_hi_fixup |
| { |
| /* Next HI fixup. */ |
| struct mips_hi_fixup *next; |
| /* This fixup. */ |
| fixS *fixp; |
| /* The section this fixup is in. */ |
| segT seg; |
| }; |
| |
| /* The list of unmatched HI relocs. */ |
| |
| static struct mips_hi_fixup *mips_hi_fixup_list; |
| |
| /* 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 microMIPS register numbers to normal MIPS register numbers. */ |
| |
| #define micromips_to_32_reg_d_map mips16_to_32_reg_map |
| |
| /* The microMIPS registers with type h. */ |
| static const unsigned int micromips_to_32_reg_h_map1[] = |
| { |
| 5, 5, 6, 4, 4, 4, 4, 4 |
| }; |
| static const unsigned int micromips_to_32_reg_h_map2[] = |
| { |
| 6, 7, 7, 21, 22, 5, 6, 7 |
| }; |
| |
| /* The microMIPS registers with type m. */ |
| static const unsigned int micromips_to_32_reg_m_map[] = |
| { |
| 0, 17, 2, 3, 16, 18, 19, 20 |
| }; |
| |
| #define micromips_to_32_reg_n_map micromips_to_32_reg_m_map |
| |
| /* Classifies the kind of instructions we're interested in when |
| implementing -mfix-vr4120. */ |
| enum fix_vr4120_class |
| { |
| FIX_VR4120_MACC, |
| FIX_VR4120_DMACC, |
| FIX_VR4120_MULT, |
| FIX_VR4120_DMULT, |
| FIX_VR4120_DIV, |
| FIX_VR4120_MTHILO, |
| NUM_FIX_VR4120_CLASSES |
| }; |
| |
| /* ...likewise -mfix-loongson2f-jump. */ |
| static bool mips_fix_loongson2f_jump; |
| |
| /* ...likewise -mfix-loongson2f-nop. */ |
| static bool mips_fix_loongson2f_nop; |
| |
| /* True if -mfix-loongson2f-nop or -mfix-loongson2f-jump passed. */ |
| static bool mips_fix_loongson2f; |
| |
| /* Given two FIX_VR4120_* values X and Y, bit Y of element X is set if |
| there must be at least one other instruction between an instruction |
| of type X and an instruction of type Y. */ |
| static unsigned int vr4120_conflicts[NUM_FIX_VR4120_CLASSES]; |
| |
| /* True if -mfix-vr4120 is in force. */ |
| static int mips_fix_vr4120; |
| |
| /* ...likewise -mfix-vr4130. */ |
| static int mips_fix_vr4130; |
| |
| /* ...likewise -mfix-24k. */ |
| static int mips_fix_24k; |
| |
| /* ...likewise -mfix-rm7000 */ |
| static int mips_fix_rm7000; |
| |
| /* ...likewise -mfix-cn63xxp1 */ |
| static bool mips_fix_cn63xxp1; |
| |
| /* ...likewise -mfix-r5900 */ |
| static bool mips_fix_r5900; |
| static bool mips_fix_r5900_explicit; |
| |
| /* ...likewise -mfix-loongson3-llsc. */ |
| static bool mips_fix_loongson3_llsc = DEFAULT_MIPS_FIX_LOONGSON3_LLSC; |
| |
| /* We don't relax branches by default, since this causes us to expand |
| `la .l2 - .l1' if there's a branch between .l1 and .l2, because we |
| fail to compute the offset before expanding the macro to the most |
| efficient expansion. */ |
| |
| static int mips_relax_branch; |
| |
| /* TRUE if checks are suppressed for invalid branches between ISA modes. |
| Needed for broken assembly produced by some GCC versions and some |
| sloppy code out there, where branches to data labels are present. */ |
| static bool mips_ignore_branch_isa; |
| |
| /* The expansion of many macros depends on the type of symbol that |
| they refer to. For example, when generating position-dependent code, |
| a macro that refers to a symbol may have two different expansions, |
| one which uses GP-relative addresses and one which uses absolute |
| addresses. When generating SVR4-style PIC, a macro may have |
| different expansions for local and global symbols. |
| |
| We handle these situations by generating both sequences and putting |
| them in variant frags. In position-dependent code, the first sequence |
| will be the GP-relative one and the second sequence will be the |
| absolute one. In SVR4 PIC, the first sequence will be for global |
| symbols and the second will be for local symbols. |
| |
| The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and |
| SECOND are the lengths of the two sequences in bytes. These fields |
| can be extracted using RELAX_FIRST() and RELAX_SECOND(). In addition, |
| the subtype has the following flags: |
| |
| RELAX_PIC |
| Set if generating PIC code. |
| |
| RELAX_USE_SECOND |
| Set if it has been decided that we should use the second |
| sequence instead of the first. |
| |
| RELAX_SECOND_LONGER |
| Set in the first variant frag if the macro's second implementation |
| is longer than its first. This refers to the macro as a whole, |
| not an individual relaxation. |
| |
| RELAX_NOMACRO |
| Set in the first variant frag if the macro appeared in a .set nomacro |
| block and if one alternative requires a warning but the other does not. |
| |
| RELAX_DELAY_SLOT |
| Like RELAX_NOMACRO, but indicates that the macro appears in a branch |
| delay slot. |
| |
| RELAX_DELAY_SLOT_16BIT |
| Like RELAX_DELAY_SLOT, but indicates that the delay slot requires a |
| 16-bit instruction. |
| |
| RELAX_DELAY_SLOT_SIZE_FIRST |
| Like RELAX_DELAY_SLOT, but indicates that the first implementation of |
| the macro is of the wrong size for the branch delay slot. |
| |
| RELAX_DELAY_SLOT_SIZE_SECOND |
| Like RELAX_DELAY_SLOT, but indicates that the second implementation of |
| the macro is of the wrong size for the branch delay slot. |
| |
| The frag's "opcode" points to the first fixup for relaxable code. |
| |
| Relaxable macros are generated using a sequence such as: |
| |
| relax_start (SYMBOL); |
| ... generate first expansion ... |
| relax_switch (); |
| ... generate second expansion ... |
| relax_end (); |
| |
| The code and fixups for the unwanted alternative are discarded |
| by md_convert_frag. */ |
| #define RELAX_ENCODE(FIRST, SECOND, PIC) \ |
| (((FIRST) << 8) | (SECOND) | ((PIC) ? 0x10000 : 0)) |
| |
| #define RELAX_FIRST(X) (((X) >> 8) & 0xff) |
| #define RELAX_SECOND(X) ((X) & 0xff) |
| #define RELAX_PIC(X) (((X) & 0x10000) != 0) |
| #define RELAX_USE_SECOND 0x20000 |
| #define RELAX_SECOND_LONGER 0x40000 |
| #define RELAX_NOMACRO 0x80000 |
| #define RELAX_DELAY_SLOT 0x100000 |
| #define RELAX_DELAY_SLOT_16BIT 0x200000 |
| #define RELAX_DELAY_SLOT_SIZE_FIRST 0x400000 |
| #define RELAX_DELAY_SLOT_SIZE_SECOND 0x800000 |
| |
| /* Branch without likely bit. If label is out of range, we turn: |
| |
| beq reg1, reg2, label |
| delay slot |
| |
| into |
| |
| bne reg1, reg2, 0f |
| nop |
| j label |
| 0: delay slot |
| |
| with the following opcode replacements: |
| |
| beq <-> bne |
| blez <-> bgtz |
| bltz <-> bgez |
| bc1f <-> bc1t |
| |
| bltzal <-> bgezal (with jal label instead of j label) |
| |
| Even though keeping the delay slot instruction in the delay slot of |
| the branch would be more efficient, it would be very tricky to do |
| correctly, because we'd have to introduce a variable frag *after* |
| the delay slot instruction, and expand that instead. Let's do it |
| the easy way for now, even if the branch-not-taken case now costs |
| one additional instruction. Out-of-range branches are not supposed |
| to be common, anyway. |
| |
| Branch likely. If label is out of range, we turn: |
| |
| beql reg1, reg2, label |
| delay slot (annulled if branch not taken) |
| |
| into |
| |
| beql reg1, reg2, 1f |
| nop |
| beql $0, $0, 2f |
| nop |
| 1: j[al] label |
| delay slot (executed only if branch taken) |
| 2: |
| |
| It would be possible to generate a shorter sequence by losing the |
| likely bit, generating something like: |
| |
| bne reg1, reg2, 0f |
| nop |
| j[al] label |
| delay slot (executed only if branch taken) |
| 0: |
| |
| beql -> bne |
| bnel -> beq |
| blezl -> bgtz |
| bgtzl -> blez |
| bltzl -> bgez |
| bgezl -> bltz |
| bc1fl -> bc1t |
| bc1tl -> bc1f |
| |
| bltzall -> bgezal (with jal label instead of j label) |
| bgezall -> bltzal (ditto) |
| |
| |
| but it's not clear that it would actually improve performance. */ |
| #define RELAX_BRANCH_ENCODE(at, pic, \ |
| uncond, likely, link, toofar) \ |
| ((relax_substateT) \ |
| (0xc0000000 \ |
| | ((at) & 0x1f) \ |
| | ((pic) ? 0x20 : 0) \ |
| | ((toofar) ? 0x40 : 0) \ |
| | ((link) ? 0x80 : 0) \ |
| | ((likely) ? 0x100 : 0) \ |
| | ((uncond) ? 0x200 : 0))) |
| #define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000) |
| #define RELAX_BRANCH_UNCOND(i) (((i) & 0x200) != 0) |
| #define RELAX_BRANCH_LIKELY(i) (((i) & 0x100) != 0) |
| #define RELAX_BRANCH_LINK(i) (((i) & 0x80) != 0) |
| #define RELAX_BRANCH_TOOFAR(i) (((i) & 0x40) != 0) |
| #define RELAX_BRANCH_PIC(i) (((i) & 0x20) != 0) |
| #define RELAX_BRANCH_AT(i) ((i) & 0x1f) |
| |
| /* For mips16 code, we use an entirely different form of relaxation. |
| mips16 supports two versions of most instructions which take |
| immediate values: a small one which takes some small value, and a |
| larger one which takes a 16 bit value. Since branches also follow |
| this pattern, relaxing these values is required. |
| |
| We can assemble both mips16 and normal MIPS code in a single |
| object. Therefore, we need to support this type of relaxation at |
| the same time that we support the relaxation described above. We |
| use the high bit of the subtype field to distinguish these cases. |
| |
| The information we store for this type of relaxation is the |
| argument code found in the opcode file for this relocation, whether |
| the user explicitly requested a small or extended form, and whether |
| the relocation is in a jump or jal delay slot. That tells us the |
| size of the value, and how it should be stored. We also store |
| whether the fragment is considered to be extended or not. We also |
| store whether this is known to be a branch to a different section, |
| whether we have tried to relax this frag yet, and whether we have |
| ever extended a PC relative fragment because of a shift count. */ |
| #define RELAX_MIPS16_ENCODE(type, e2, pic, sym32, nomacro, \ |
| small, ext, \ |
| dslot, jal_dslot) \ |
| (0x80000000 \ |
| | ((type) & 0xff) \ |
| | ((e2) ? 0x100 : 0) \ |
| | ((pic) ? 0x200 : 0) \ |
| | ((sym32) ? 0x400 : 0) \ |
| | ((nomacro) ? 0x800 : 0) \ |
| | ((small) ? 0x1000 : 0) \ |
| | ((ext) ? 0x2000 : 0) \ |
| | ((dslot) ? 0x4000 : 0) \ |
| | ((jal_dslot) ? 0x8000 : 0)) |
| |
| #define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000) |
| #define RELAX_MIPS16_TYPE(i) ((i) & 0xff) |
| #define RELAX_MIPS16_E2(i) (((i) & 0x100) != 0) |
| #define RELAX_MIPS16_PIC(i) (((i) & 0x200) != 0) |
| #define RELAX_MIPS16_SYM32(i) (((i) & 0x400) != 0) |
| #define RELAX_MIPS16_NOMACRO(i) (((i) & 0x800) != 0) |
| #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x1000) != 0) |
| #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x2000) != 0) |
| #define RELAX_MIPS16_DSLOT(i) (((i) & 0x4000) != 0) |
| #define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x8000) != 0) |
| |
| #define RELAX_MIPS16_EXTENDED(i) (((i) & 0x10000) != 0) |
| #define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x10000) |
| #define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) & ~0x10000) |
| #define RELAX_MIPS16_ALWAYS_EXTENDED(i) (((i) & 0x20000) != 0) |
| #define RELAX_MIPS16_MARK_ALWAYS_EXTENDED(i) ((i) | 0x20000) |
| #define RELAX_MIPS16_CLEAR_ALWAYS_EXTENDED(i) ((i) & ~0x20000) |
| #define RELAX_MIPS16_MACRO(i) (((i) & 0x40000) != 0) |
| #define RELAX_MIPS16_MARK_MACRO(i) ((i) | 0x40000) |
| #define RELAX_MIPS16_CLEAR_MACRO(i) ((i) & ~0x40000) |
| |
| /* For microMIPS code, we use relaxation similar to one we use for |
| MIPS16 code. Some instructions that take immediate values support |
| two encodings: a small one which takes some small value, and a |
| larger one which takes a 16 bit value. As some branches also follow |
| this pattern, relaxing these values is required. |
| |
| We can assemble both microMIPS and normal MIPS code in a single |
| object. Therefore, we need to support this type of relaxation at |
| the same time that we support the relaxation described above. We |
| use one of the high bits of the subtype field to distinguish these |
| cases. |
| |
| 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 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, pic, \ |
| uncond, compact, link, nods, \ |
| relax32, toofar16, toofar32) \ |
| (0x40000000 \ |
| | ((type) & 0xff) \ |
| | (((at) & 0x1f) << 8) \ |
| | ((insn32) ? 0x2000 : 0) \ |
| | ((pic) ? 0x4000 : 0) \ |
| | ((uncond) ? 0x8000 : 0) \ |
| | ((compact) ? 0x10000 : 0) \ |
| | ((link) ? 0x20000 : 0) \ |
| | ((nods) ? 0x40000 : 0) \ |
| | ((relax32) ? 0x80000 : 0) \ |
| | ((toofar16) ? 0x100000 : 0) \ |
| | ((toofar32) ? 0x200000 : 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_INSN32(i) (((i) & 0x2000) != 0) |
| #define RELAX_MICROMIPS_PIC(i) (((i) & 0x4000) != 0) |
| #define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x8000) != 0) |
| #define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x10000) != 0) |
| #define RELAX_MICROMIPS_LINK(i) (((i) & 0x20000) != 0) |
| #define RELAX_MICROMIPS_NODS(i) (((i) & 0x40000) != 0) |
| #define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x80000) != 0) |
| |
| #define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x100000) != 0) |
| #define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x100000) |
| #define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x100000) |
| #define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x200000) != 0) |
| #define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x200000) |
| #define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x200000) |
| |
| /* 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) \ |
| (((x) &~ (offsetT) 0x7fffffff) == 0 \ |
| || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff)) |
| |
| /* Is the given value a sign-extended 16-bit value? */ |
| #define IS_SEXT_16BIT_NUM(x) \ |
| (((x) &~ (offsetT) 0x7fff) == 0 \ |
| || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff)) |
| |
| /* Is the given value a sign-extended 12-bit value? */ |
| #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)) |
| |
| /* Extract bits MASK << SHIFT from STRUCT and shift them right |
| SHIFT places. */ |
| #define EXTRACT_BITS(STRUCT, MASK, SHIFT) \ |
| (((STRUCT) >> (SHIFT)) & (MASK)) |
| |
| /* Extract the operand given by FIELD from mips_cl_insn INSN. */ |
| #define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \ |
| (!(MICROMIPS) \ |
| ? EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD) \ |
| : EXTRACT_BITS ((INSN).insn_opcode, \ |
| MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD)) |
| #define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \ |
| EXTRACT_BITS ((INSN).insn_opcode, \ |
| MIPS16OP_MASK_##FIELD, \ |
| MIPS16OP_SH_##FIELD) |
| |
| /* The MIPS16 EXTEND opcode, shifted left 16 places. */ |
| #define MIPS16_EXTEND (0xf000U << 16) |
| |
| /* Whether or not we are emitting a branch-likely macro. */ |
| static bool emit_branch_likely_macro = false; |
| |
| /* Global variables used when generating relaxable macros. See the |
| comment above RELAX_ENCODE for more details about how relaxation |
| is used. */ |
| static struct { |
| /* 0 if we're not emitting a relaxable macro. |
| 1 if we're emitting the first of the two relaxation alternatives. |
| 2 if we're emitting the second alternative. */ |
| int sequence; |
| |
| /* The first relaxable fixup in the current frag. (In other words, |
| the first fixup that refers to relaxable code.) */ |
| fixS *first_fixup; |
| |
| /* sizes[0] says how many bytes of the first alternative are stored in |
| the current frag. Likewise sizes[1] for the second alternative. */ |
| unsigned int sizes[2]; |
| |
| /* The symbol on which the choice of sequence depends. */ |
| symbolS *symbol; |
| } mips_relax; |
| |
| /* Global variables used to decide whether a macro needs a warning. */ |
| static struct { |
| /* True if the macro is in a branch delay slot. */ |
| bool delay_slot_p; |
| |
| /* Set to the length in bytes required if the macro is in a delay slot |
| that requires a specific length of instruction, otherwise zero. */ |
| unsigned int delay_slot_length; |
| |
| /* For relaxable macros, sizes[0] is the length of the first alternative |
| in bytes and sizes[1] is the length of the second alternative. |
| For non-relaxable macros, both elements give the length of the |
| macro in bytes. */ |
| unsigned int sizes[2]; |
| |
| /* For relaxable macros, first_insn_sizes[0] is the length of the first |
| instruction of the first alternative in bytes and first_insn_sizes[1] |
| is the length of the first instruction of the second alternative. |
| For non-relaxable macros, both elements give the length of the first |
| instruction in bytes. |
| |
| Set to zero if we haven't yet seen the first instruction. */ |
| unsigned int first_insn_sizes[2]; |
| |
| /* For relaxable macros, insns[0] is the number of instructions for the |
| first alternative and insns[1] is the number of instructions for the |
| second alternative. |
| |
| For non-relaxable macros, both elements give the number of |
| instructions for the macro. */ |
| unsigned int insns[2]; |
| |
| /* The first variant frag for this macro. */ |
| fragS *first_frag; |
| } mips_macro_warning; |
| |
| /* Prototypes for static functions. */ |
| |
| enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG }; |
| |
| static void append_insn |
| (struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *, |
| bool expansionp); |
| static void mips_no_prev_insn (void); |
| static void macro_build (expressionS *, const char *, const char *, ...); |
| static void mips16_macro_build |
| (expressionS *, const char *, const char *, va_list *); |
| 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, 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 unsigned long mips16_immed_extend (offsetT, unsigned int); |
| static void mips16_immed |
| (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_align (int); |
| static void s_change_sec (int); |
| static void s_change_section (int); |
| static void s_cons (int); |
| static void s_float_cons (int); |
| static void s_mips_globl (int); |
| static void s_option (int); |
| static void s_mipsset (int); |
| static void s_abicalls (int); |
| static void s_cpload (int); |
| static void s_cpsetup (int); |
| static void s_cplocal (int); |
| static void s_cprestore (int); |
| 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 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_mask (int reg_type); |
| static void s_mips_stab (int); |
| static void s_mips_weakext (int); |
| static void s_mips_file (int); |
| static void s_mips_loc (int); |
| static bool pic_need_relax (symbolS *); |
| static int relaxed_branch_length (fragS *, asection *, int); |
| 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; /* 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.) */ |
| |
| 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); |
| |
| /* Command-line options. */ |
| 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_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_MIPS16E2, |
| OPTION_NO_MIPS16E2, |
| OPTION_CRC, |
| OPTION_NO_CRC, |
| 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_LOONGSON3_LLSC, |
| OPTION_NO_FIX_LOONGSON3_LLSC, |
| 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_FIX_R5900, |
| OPTION_NO_FIX_R5900, |
| 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_IGNORE_BRANCH_ISA, |
| OPTION_NO_IGNORE_BRANCH_ISA, |
| 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_GINV, |
| OPTION_NO_GINV, |
| OPTION_LOONGSON_MMI, |
| OPTION_NO_LOONGSON_MMI, |
| OPTION_LOONGSON_CAM, |
| OPTION_NO_LOONGSON_CAM, |
| OPTION_LOONGSON_EXT, |
| OPTION_NO_LOONGSON_EXT, |
| OPTION_LOONGSON_EXT2, |
| OPTION_NO_LOONGSON_EXT2, |
| 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}, |
| {"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}, |
| |
| /* 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}, |
| {"mmips16e2", no_argument, NULL, OPTION_MIPS16E2}, |
| {"mno-mips16e2", no_argument, NULL, OPTION_NO_MIPS16E2}, |
| {"mcrc", no_argument, NULL, OPTION_CRC}, |
| {"mno-crc", no_argument, NULL, OPTION_NO_CRC}, |
| {"mginv", no_argument, NULL, OPTION_GINV}, |
| {"mno-ginv", no_argument, NULL, OPTION_NO_GINV}, |
| {"mloongson-mmi", no_argument, NULL, OPTION_LOONGSON_MMI}, |
| {"mno-loongson-mmi", no_argument, NULL, OPTION_NO_LOONGSON_MMI}, |
| {"mloongson-cam", no_argument, NULL, OPTION_LOONGSON_CAM}, |
| {"mno-loongson-cam", no_argument, NULL, OPTION_NO_LOONGSON_CAM}, |
| {"mloongson-ext", no_argument, NULL, OPTION_LOONGSON_EXT}, |
| {"mno-loongson-ext", no_argument, NULL, OPTION_NO_LOONGSON_EXT}, |
| {"mloongson-ext2", no_argument, NULL, OPTION_LOONGSON_EXT2}, |
| {"mno-loongson-ext2", no_argument, NULL, OPTION_NO_LOONGSON_EXT2}, |
| |
| /* 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-loongson3-llsc", no_argument, NULL, OPTION_FIX_LOONGSON3_LLSC}, |
| {"mno-fix-loongson3-llsc", no_argument, NULL, OPTION_NO_FIX_LOONGSON3_LLSC}, |
| {"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}, |
| {"mfix-r5900", no_argument, NULL, OPTION_FIX_R5900}, |
| {"mno-fix-r5900", no_argument, NULL, OPTION_NO_FIX_R5900}, |
| |
| /* 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}, |
| {"mignore-branch-isa", no_argument, NULL, OPTION_IGNORE_BRANCH_ISA}, |
| {"mno-ignore-branch-isa", no_argument, NULL, OPTION_NO_IGNORE_BRANCH_ISA}, |
| {"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); |
| |
| /* 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, 2, 2, |
| -1 }, |
| |
| { "mips16e2", ASE_MIPS16E2, 0, |
| OPTION_MIPS16E2, OPTION_NO_MIPS16E2, |
| 2, 2, -1, -1, |
| 6 }, |
| |
| { "crc", ASE_CRC, ASE_CRC64, |
| OPTION_CRC, OPTION_NO_CRC, |
| 6, 6, -1, -1, |
| -1 }, |
| |
| { "ginv", ASE_GINV, 0, |
| OPTION_GINV, OPTION_NO_GINV, |
| 6, 6, 6, 6, |
| -1 }, |
| |
| { "loongson-mmi", ASE_LOONGSON_MMI, 0, |
| OPTION_LOONGSON_MMI, OPTION_NO_LOONGSON_MMI, |
| 0, 0, -1, -1, |
| -1 }, |
| |
| { "loongson-cam", ASE_LOONGSON_CAM, 0, |
| OPTION_LOONGSON_CAM, OPTION_NO_LOONGSON_CAM, |
| 0, 0, -1, -1, |
| -1 }, |
| |
| { "loongson-ext", ASE_LOONGSON_EXT, 0, |
| OPTION_LOONGSON_EXT, OPTION_NO_LOONGSON_EXT, |
| 0, 0, -1, -1, |
| -1 }, |
| |
| { "loongson-ext2", ASE_LOONGSON_EXT | ASE_LOONGSON_EXT2, 0, |
| OPTION_LOONGSON_EXT2, OPTION_NO_LOONGSON_EXT2, |
| 0, 0, -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, |
| ASE_LOONGSON_EXT | ASE_LOONGSON_EXT2 |
| }; |
| |
| /* 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}, |
| {"data", s_change_sec, 'd'}, |
| {"double", s_float_cons, 'd'}, |
| {"float", s_float_cons, 'f'}, |
| {"globl", s_mips_globl, 0}, |
| {"global", s_mips_globl, 0}, |
| {"hword", s_cons, 1}, |
| {"int", s_cons, 2}, |
| {"long", s_cons, 2}, |
| {"octa", s_cons, 4}, |
| {"quad", s_cons, 3}, |
| {"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}, |
| |
| { "extern", ecoff_directive_extern, 0}, |
| |
| { NULL, NULL, 0 }, |
| }; |
| |
| static const pseudo_typeS mips_nonecoff_pseudo_table[] = |
| { |
| /* These pseudo-ops should be defined by the object file format. |
| However, a.out doesn't support them, so we have versions here. */ |
| {"aent", s_mips_ent, 1}, |
| {"bgnb", s_ignore, 0}, |
| {"end", s_mips_end, 0}, |
| {"endb", s_ignore, 0}, |
| {"ent", s_mips_ent, 0}, |
| {"file", s_mips_file, 0}, |
| {"fmask", s_mips_mask, 'F'}, |
| {"frame", s_mips_frame, 0}, |
| {"loc", s_mips_loc, 0}, |
| {"mask", s_mips_mask, 'R'}, |
| {"verstamp", s_ignore, 0}, |
| { NULL, NULL, 0 }, |
| }; |
| |
| /* Export the ABI address size for use by TC_ADDRESS_BYTES for the |
| purpose of the `.dc.a' internal pseudo-op. */ |
| |
| int |
| mips_address_bytes (void) |
| { |
| file_mips_check_options (); |
| return HAVE_64BIT_ADDRESSES ? 8 : 4; |
| } |
| |
| extern void pop_insert (const pseudo_typeS *); |
| |
| void |
| mips_pop_insert (void) |
| { |
| pop_insert (mips_pseudo_table); |
| if (! ECOFF_DEBUGGING) |
| pop_insert (mips_nonecoff_pseudo_table); |
| } |
| |
| /* Symbols labelling the current insn. */ |
| |
| struct insn_label_list |
| { |
| struct insn_label_list *next; |
| symbolS *label; |
| }; |
| |
| static struct insn_label_list *free_insn_labels; |
| #define label_list tc_segment_info_data.labels |
| |
| static void mips_clear_insn_labels (void); |
| static void mips_mark_labels (void); |
| static void mips_compressed_mark_labels (void); |
| |
| static inline void |
| mips_clear_insn_labels (void) |
| { |
| 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; |
| } |
| } |
| |
| /* Mark instruction labels in MIPS16/microMIPS mode. */ |
| |
| static inline void |
| mips_mark_labels (void) |
| { |
| if (HAVE_CODE_COMPRESSION) |
| mips_compressed_mark_labels (); |
| } |
| |
| static char *expr_end; |
| |
| /* 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; |
| |
| /* 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 expressionS offset_expr; |
| static bfd_reloc_code_real_type offset_reloc[3] |
| = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; |
| |
| /* This is set to the resulting size of the instruction to be produced |
| by mips16_ip if an explicit extension is used or by mips_ip if an |
| explicit size is supplied. */ |
| |
| static unsigned int forced_insn_length; |
| |
| /* True if we are assembling an instruction. All dot symbols defined during |
| this time should be treated as code labels. */ |
| |
| static bool mips_assembling_insn; |
| |
| /* The pdr segment for per procedure frame/regmask info. Not used for |
| ECOFF debugging. */ |
| |
| static segT pdr_seg; |
| |
| /* The default target format to use. */ |
| |
| #if defined (TE_FreeBSD) |
| #define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips-freebsd" |
| #elif defined (TE_TMIPS) |
| #define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips" |
| #else |
| #define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "mips" |
| #endif |
| |
| const char * |
| mips_target_format (void) |
| { |
| switch (OUTPUT_FLAVOR) |
| { |
| case bfd_target_elf_flavour: |
| #ifdef TE_VXWORKS |
| if (!HAVE_64BIT_OBJECTS && !HAVE_NEWABI) |
| return (target_big_endian |
| ? "elf32-bigmips-vxworks" |
| : "elf32-littlemips-vxworks"); |
| #endif |
| return (target_big_endian |
| ? (HAVE_64BIT_OBJECTS |
| ? ELF_TARGET ("elf64-", "big") |
| : (HAVE_NEWABI |
| ? ELF_TARGET ("elf32-n", "big") |
| : ELF_TARGET ("elf32-", "big"))) |
| : (HAVE_64BIT_OBJECTS |
| ? ELF_TARGET ("elf64-", "little") |
| : (HAVE_NEWABI |
| ? ELF_TARGET ("elf32-n", "little") |
| : ELF_TARGET ("elf32-", "little")))); |
| default: |
| abort (); |
| return NULL; |
| } |
| } |
| |
| /* 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, |
| bool enabled_p) |
| { |
| unsigned int mask; |
| |
| mask = mips_ase_mask (ase->flags); |
| opts->ase &= ~mask; |
| |
| /* Clear combination ASE flags, which need to be recalculated based on |
| updated regular ASE settings. */ |
| opts->ase &= ~(ASE_MIPS16E2_MT | ASE_XPA_VIRT | ASE_EVA_R6); |
| |
| if (enabled_p) |
| opts->ase |= ase->flags; |
| |
| /* The Virtualization ASE has eXtended Physical Addressing (XPA) |
| instructions which are only valid when both ASEs are enabled. |
| This sets the ASE_XPA_VIRT flag when both ASEs are present. */ |
| if ((opts->ase & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT)) |
| { |
| opts->ase |= ASE_XPA_VIRT; |
| mask |= ASE_XPA_VIRT; |
| } |
| if ((opts->ase & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT)) |
| { |
| opts->ase |= ASE_MIPS16E2_MT; |
| mask |= ASE_MIPS16E2_MT; |
| } |
| |
| /* The EVA Extension has instructions which are only valid when the R6 ISA |
| is enabled. This sets the ASE_EVA_R6 flag when both EVA and R6 ISA are |
| present. */ |
| if (((opts->ase & ASE_EVA) != 0) && ISA_IS_R6 (opts->isa)) |
| { |
| opts->ase |= ASE_EVA_R6; |
| mask |= ASE_EVA_R6; |
| } |
| |
| 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 it is a 32-bit instruction. */ |
| |
| static inline unsigned int |
| micromips_insn_length (const struct mips_opcode *mo) |
| { |
| 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. */ |
| |
| static inline unsigned int |
| insn_length (const struct mips_cl_insn *insn) |
| { |
| if (mips_opts.micromips) |
| return micromips_insn_length (insn->insn_mo); |
| else if (mips_opts.mips16) |
| return mips16_opcode_length (insn->insn_opcode); |
| else |
| return 4; |
| } |
| |
| /* Initialise INSN from opcode entry MO. Leave its position unspecified. */ |
| |
| static void |
| create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo) |
| { |
| size_t i; |
| |
| insn->insn_mo = mo; |
| insn->insn_opcode = mo->match; |
| insn->frag = NULL; |
| insn->where = 0; |
| for (i = 0; i < ARRAY_SIZE (insn->fixp); i++) |
| insn->fixp[i] = NULL; |
| insn->fixed_p = (mips_opts.noreorder > 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) |
| { |
| if (mips_opts.mips16 |
| && operand->type == OP_INT && operand->lsb == 0 |
| && mips_opcode_32bit_p (insn->insn_mo)) |
| insn->insn_opcode |= mips16_immed_extend (uval, operand->size); |
| else |
| insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval); |
| } |
| |
| /* Extract the value of OPERAND from INSN. */ |
| |
| static inline unsigned |
| insn_extract_operand (const struct mips_cl_insn *insn, |
| const struct mips_operand *operand) |
| { |
| return mips_extract_operand (operand, insn->insn_opcode); |
| } |
| |
| /* Record the current MIPS16/microMIPS mode in now_seg. */ |
| |
| static void |
| mips_record_compressed_mode (void) |
| { |
| segment_info_type *si; |
| |
| si = seg_info (now_seg); |
| if (si->tc_segment_info_data.mips16 != mips_opts.mips16) |
| si->tc_segment_info_data.mips16 = mips_opts.mips16; |
| if (si->tc_segment_info_data.micromips != mips_opts.micromips) |
| si->tc_segment_info_data.micromips = mips_opts.micromips; |
| } |
| |
| /* Read a standard MIPS instruction from BUF. */ |
| |
| static unsigned long |
| read_insn (char *buf) |
| { |
| if (target_big_endian) |
| return bfd_getb32 ((bfd_byte *) buf); |
| else |
| return bfd_getl32 ((bfd_byte *) buf); |
| } |
| |
| /* Write standard MIPS instruction INSN to BUF. Return a pointer to |
| the next byte. */ |
| |
| static char * |
| write_insn (char *buf, unsigned int insn) |
| { |
| md_number_to_chars (buf, insn, 4); |
| return buf + 4; |
| } |
| |
| /* 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; |
| insn->fixp[i]->fx_where = where; |
| } |
| install_insn (insn); |
| } |
| |
| /* Add INSN to the end of the output. */ |
| |
| static void |
| add_fixed_insn (struct mips_cl_insn *insn) |
| { |
| char *f = frag_more (insn_length (insn)); |
| move_insn (insn, frag_now, f - frag_now->fr_literal); |
| } |
| |
| /* Start a variant frag and move INSN to the start of the variant part, |
| marking it as fixed. The other arguments are as for frag_var. */ |
| |
| static void |
| add_relaxed_insn (struct mips_cl_insn *insn, int max_chars, int var, |
| relax_substateT subtype, symbolS *symbol, offsetT offset) |
| { |
| frag_grow (max_chars); |
| move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal); |
| insn->fixed_p = 1; |
| frag_var (rs_machine_dependent, max_chars, var, |
| subtype, symbol, offset, NULL); |
| } |
| |
| /* Insert N copies of INSN into the history buffer, starting at |
| position FIRST. Neither FIRST nor N need to be clipped. */ |
| |
| static void |
| insert_into_history (unsigned int first, unsigned int n, |
| const struct mips_cl_insn *insn) |
| { |
| if (mips_relax.sequence != 2) |
| { |
| unsigned int i; |
| |
| for (i = ARRAY_SIZE (history); i-- > first;) |
| if (i >= first + n) |
| history[i] = history[i - n]; |
| else |
| history[i] = *insn; |
| } |
| } |
| |
| /* 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 bool |
| 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. */ |
| |
| static void |
| init_vr4120_conflicts (void) |
| { |
| #define CONFLICT(FIRST, SECOND) \ |
| vr4120_conflicts[FIX_VR4120_##FIRST] |= 1 << FIX_VR4120_##SECOND |
| |
| /* Errata 21 - [D]DIV[U] after [D]MACC */ |
| CONFLICT (MACC, DIV); |
| CONFLICT (DMACC, DIV); |
| |
| /* Errata 23 - Continuous DMULT[U]/DMACC instructions. */ |
| CONFLICT (DMULT, DMULT); |
| CONFLICT (DMULT, DMACC); |
| CONFLICT (DMACC, DMULT); |
| CONFLICT (DMACC, DMACC); |
| |
| /* Errata 24 - MT{LO,HI} after [D]MACC */ |
| CONFLICT (MACC, MTHILO); |
| CONFLICT (DMACC, MTHILO); |
| |
| /* VR4181A errata MD(1): "If a MULT, MULTU, DMULT or DMULTU |
| instruction is executed immediately after a MACC or DMACC |
| instruction, the result of [either instruction] is incorrect." */ |
| CONFLICT (MACC, MULT); |
| CONFLICT (MACC, DMULT); |
| CONFLICT (DMACC, MULT); |
| CONFLICT (DMACC, DMULT); |
| |
| /* VR4181A errata MD(4): "If a MACC or DMACC instruction is |
| executed immediately after a DMULT, DMULTU, DIV, DIVU, |
| DDIV or DDIVU instruction, the result of the MACC or |
| DMACC instruction is incorrect.". */ |
| CONFLICT (DMULT, MACC); |
| CONFLICT (DMULT, DMACC); |
| CONFLICT (DIV, MACC); |
| CONFLICT (DIV, DMACC); |
| |
| #undef CONFLICT |
| } |
| |
| struct regname { |
| const char *name; |
| unsigned int num; |
| }; |
| |
| #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}, \ |
| {"$1", RTYPE_NUM | 1}, \ |
| {"$2", RTYPE_NUM | 2}, \ |
| {"$3", RTYPE_NUM | 3}, \ |
| {"$4", RTYPE_NUM | 4}, \ |
| {"$5", RTYPE_NUM | 5}, \ |
| {"$6", RTYPE_NUM | 6}, \ |
| {"$7", RTYPE_NUM | 7}, \ |
| {"$8", RTYPE_NUM | 8}, \ |
| {"$9", RTYPE_NUM | 9}, \ |
| {"$10", RTYPE_NUM | 10}, \ |
| {"$11", RTYPE_NUM | 11}, \ |
| {"$12", RTYPE_NUM | 12}, \ |
| {"$13", RTYPE_NUM | 13}, \ |
| {"$14", RTYPE_NUM | 14}, \ |
| {"$15", RTYPE_NUM | 15}, \ |
| {"$16", RTYPE_NUM | 16}, \ |
| {"$17", RTYPE_NUM | 17}, \ |
| {"$18", RTYPE_NUM | 18}, \ |
| {"$19", RTYPE_NUM | 19}, \ |
| {"$20", RTYPE_NUM | 20}, \ |
| {"$21", RTYPE_NUM | 21}, \ |
| {"$22", RTYPE_NUM | 22}, \ |
| {"$23", RTYPE_NUM | 23}, \ |
| {"$24", RTYPE_NUM | 24}, \ |
| {"$25", RTYPE_NUM | 25}, \ |
| {"$26", RTYPE_NUM | 26}, \ |
| {"$27", RTYPE_NUM | 27}, \ |
| {"$28", RTYPE_NUM | 28}, \ |
| {"$29", RTYPE_NUM | 29}, \ |
| {"$30", RTYPE_NUM | 30}, \ |
| {"$31", RTYPE_NUM | 31} |
| |
| #define FPU_REGISTER_NAMES \ |
| {"$f0", RTYPE_FPU | 0}, \ |
| {"$f1", RTYPE_FPU | 1}, \ |
| {"$f2", RTYPE_FPU | 2}, \ |
| {"$f3", RTYPE_FPU | 3}, \ |
| {"$f4", RTYPE_FPU | 4}, \ |
| {"$f5", RTYPE_FPU | 5}, \ |
| {"$f6", RTYPE_FPU | 6}, \ |
| {"$f7", RTYPE_FPU | 7}, \ |
| {"$f8", RTYPE_FPU | 8}, \ |
| {"$f9", RTYPE_FPU | 9}, \ |
| {"$f10", RTYPE_FPU | 10}, \ |
| {"$f11", RTYPE_FPU | 11}, \ |
| {"$f12", RTYPE_FPU | 12}, \ |
| {"$f13", RTYPE_FPU | 13}, \ |
| {"$f14", RTYPE_FPU | 14}, \ |
| {"$f15", RTYPE_FPU | 15}, \ |
| {"$f16", RTYPE_FPU | 16}, \ |
| {"$f17", RTYPE_FPU | 17}, \ |
| {"$f18", RTYPE_FPU | 18}, \ |
| {"$f19", RTYPE_FPU | 19}, \ |
| {"$f20", RTYPE_FPU | 20}, \ |
| {"$f21", RTYPE_FPU | 21}, \ |
| {"$f22", RTYPE_FPU | 22}, \ |
| {"$f23", RTYPE_FPU | 23}, \ |
| {"$f24", RTYPE_FPU | 24}, \ |
| {"$f25", RTYPE_FPU | 25}, \ |
| {"$f26", RTYPE_FPU | 26}, \ |
| {"$f27", RTYPE_FPU | 27}, \ |
| {"$f28", RTYPE_FPU | 28}, \ |
| {"$f29", RTYPE_FPU | 29}, \ |
| {"$f30", RTYPE_FPU | 30}, \ |
| {"$f31", RTYPE_FPU | 31} |
| |
| #define FPU_CONDITION_CODE_NAMES \ |
| {"$fcc0", RTYPE_FCC | 0}, \ |
| {"$fcc1", RTYPE_FCC | 1}, \ |
| {"$fcc2", RTYPE_FCC | 2}, \ |
| {"$fcc3", RTYPE_FCC | 3}, \ |
| {"$fcc4", RTYPE_FCC | 4}, \ |
| {"$fcc5", RTYPE_FCC | 5}, \ |
| {"$fcc6", RTYPE_FCC | 6}, \ |
| {"$fcc7", RTYPE_FCC | 7} |
| |
| #define COPROC_CONDITION_CODE_NAMES \ |
| {"$cc0", RTYPE_FCC | RTYPE_CCC | 0}, \ |
| {"$cc1", RTYPE_FCC | RTYPE_CCC | 1}, \ |
| {"$cc2", RTYPE_FCC | RTYPE_CCC | 2}, \ |
| {"$cc3", RTYPE_FCC | RTYPE_CCC | 3}, \ |
| {"$cc4", RTYPE_FCC | RTYPE_CCC | 4}, \ |
| {"$cc5", RTYPE_FCC | RTYPE_CCC | 5}, \ |
| {"$cc6", RTYPE_FCC | RTYPE_CCC | 6}, \ |
| {"$cc7", RTYPE_FCC | RTYPE_CCC | 7} |
| |
| #define N32N64_SYMBOLIC_REGISTER_NAMES \ |
| {"$a4", RTYPE_GP | 8}, \ |
| {"$a5", RTYPE_GP | 9}, \ |
| {"$a6", RTYPE_GP | 10}, \ |
| {"$a7", RTYPE_GP | 11}, \ |
| {"$ta0", RTYPE_GP | 8}, /* alias for $a4 */ \ |
| {"$ta1", RTYPE_GP | 9}, /* alias for $a5 */ \ |
| {"$ta2", RTYPE_GP | 10}, /* alias for $a6 */ \ |
| {"$ta3", RTYPE_GP | 11}, /* alias for $a7 */ \ |
| {"$t0", RTYPE_GP | 12}, \ |
| {"$t1", RTYPE_GP | 13}, \ |
| {"$t2", RTYPE_GP | 14}, \ |
| {"$t3", RTYPE_GP | 15} |
| |
| #define O32_SYMBOLIC_REGISTER_NAMES \ |
| {"$t0", RTYPE_GP | 8}, \ |
| {"$t1", RTYPE_GP | 9}, \ |
| {"$t2", RTYPE_GP | 10}, \ |
| {"$t3", RTYPE_GP | 11}, \ |
| {"$t4", RTYPE_GP | 12}, \ |
| {"$t5", RTYPE_GP | 13}, \ |
| {"$t6", RTYPE_GP | 14}, \ |
| {"$t7", RTYPE_GP | 15}, \ |
| {"$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 */ |
| |
| /* Remaining symbolic register names. */ |
| #define SYMBOLIC_REGISTER_NAMES \ |
| {"$zero", RTYPE_GP | 0}, \ |
| {"$at", RTYPE_GP | 1}, \ |
| {"$AT", RTYPE_GP | 1}, \ |
| {"$v0", RTYPE_GP | 2}, \ |
| {"$v1", RTYPE_GP | 3}, \ |
| {"$a0", RTYPE_GP | 4}, \ |
| {"$a1", RTYPE_GP | 5}, \ |
| {"$a2", RTYPE_GP | 6}, \ |
| {"$a3", RTYPE_GP | 7}, \ |
| {"$s0", RTYPE_GP | 16}, \ |
| {"$s1", RTYPE_GP | 17}, \ |
| {"$s2", RTYPE_GP | 18}, \ |
| {"$s3", RTYPE_GP | 19}, \ |
| {"$s4", RTYPE_GP | 20}, \ |
| {"$s5", RTYPE_GP | 21}, \ |
| {"$s6", RTYPE_GP | 22}, \ |
| {"$s7", RTYPE_GP | 23}, \ |
| {"$t8", RTYPE_GP | 24}, \ |
| {"$t9", RTYPE_GP | 25}, \ |
| {"$k0", RTYPE_GP | 26}, \ |
| {"$kt0", RTYPE_GP | 26}, \ |
| {"$k1", RTYPE_GP | 27}, \ |
| {"$kt1", RTYPE_GP | 27}, \ |
| {"$gp", RTYPE_GP | 28}, \ |
| {"$sp", RTYPE_GP | 29}, \ |
| {"$s8", RTYPE_GP | 30}, \ |
| {"$fp", RTYPE_GP | 30}, \ |
| {"$ra", RTYPE_GP | 31} |
| |
| #define MIPS16_SPECIAL_REGISTER_NAMES \ |
| {"$pc", RTYPE_PC | 0} |
| |
| #define MDMX_VECTOR_REGISTER_NAMES \ |
| /* {"$v0", RTYPE_VEC | 0}, Clash with REG 2 above. */ \ |
| /* {"$v1", RTYPE_VEC | 1}, Clash with REG 3 above. */ \ |
| {"$v2", RTYPE_VEC | 2}, \ |
| {"$v3", RTYPE_VEC | 3}, \ |
| {"$v4", RTYPE_VEC | 4}, \ |
| {"$v5", RTYPE_VEC | 5}, \ |
| {"$v6", RTYPE_VEC | 6}, \ |
| {"$v7", RTYPE_VEC | 7}, \ |
| {"$v8", RTYPE_VEC | 8}, \ |
| {"$v9", RTYPE_VEC | 9}, \ |
| {"$v10", RTYPE_VEC | 10}, \ |
| {"$v11", RTYPE_VEC | 11}, \ |
| {"$v12", RTYPE_VEC | 12}, \ |
| {"$v13", RTYPE_VEC | 13}, \ |
| {"$v14", RTYPE_VEC | 14}, \ |
| {"$v15", RTYPE_VEC | 15}, \ |
| {"$v16", RTYPE_VEC | 16}, \ |
| {"$v17", RTYPE_VEC | 17}, \ |
| {"$v18", RTYPE_VEC | 18}, \ |
| {"$v19", RTYPE_VEC | 19}, \ |
| {"$v20", RTYPE_VEC | 20}, \ |
| {"$v21", RTYPE_VEC | 21}, \ |
| {"$v22", RTYPE_VEC | 22}, \ |
| {"$v23", RTYPE_VEC | 23}, \ |
| {"$v24", RTYPE_VEC | 24}, \ |
| {"$v25", RTYPE_VEC | 25}, \ |
| {"$v26", RTYPE_VEC | 26}, \ |
| {"$v27", RTYPE_VEC | 27}, \ |
| {"$v28", RTYPE_VEC | 28}, \ |
| {"$v29", RTYPE_VEC | 29}, \ |
| {"$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}, \ |
| {"$ac2", RTYPE_ACC | 2}, \ |
| {"$ac3", RTYPE_ACC | 3} |
| |
| static const struct regname reg_names[] = { |
| GENERIC_REGISTER_NUMBERS, |
| FPU_REGISTER_NAMES, |
| FPU_CONDITION_CODE_NAMES, |
| COPROC_CONDITION_CODE_NAMES, |
| |
| /* 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 |
| 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} |
| }; |
| |
| static const struct regname reg_names_o32[] = { |
| O32_SYMBOLIC_REGISTER_NAMES, |
| {0, 0} |
| }; |
| |
| static const struct regname reg_names_n32n64[] = { |
| N32N64_SYMBOLIC_REGISTER_NAMES, |
| {0, 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 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 bool |
| mips_parse_register_1 (char *s, char *e, unsigned int *symval_ptr) |
| { |
| char save_c; |
| 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 bool |
| 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. */ |
| s = e = *sptr; |
| if (is_name_beginner (*e)) |
| ++e; |
| while (is_part_of_name (*e)) |
| ++e; |
| |
| 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; |
| |
| if (mips_parse_register (s, ®no, NULL)) |
| { |
| if (types & RTYPE_VEC) |
| regno = mips_prefer_vec_regno (regno); |
| if (regno & types) |
| regno &= RNUM_MASK; |
| else |
| regno = ~0; |
| } |
| else |
| { |
| if (types & RWARN) |
| as_warn (_("unrecognized register name `%s'"), *s); |
| regno = ~0; |
| } |
| if (regnop) |
| *regnop = regno; |
| return regno <= RNUM_MASK; |
| } |
| |
| /* Parse a VU0 "x?y?z?w?" channel mask at S and store the associated |
| mask in *CHANNELS. Return a pointer to the first unconsumed character. */ |
| |
| static char * |
| mips_parse_vu0_channels (char *s, unsigned int *channels) |
| { |
| unsigned int i; |
| |
| *channels = 0; |
| for (i = 0; i < 4; i++) |
| |