[PATCH v2 1/6] Gas changes for nanoMIPS support.
"Tsing" <[email protected]> Fri, 24 Jul 2026 18:30:53 +0800
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
From: tsing <[email protected]> / * configure.ac: Add nanoMIPS target. * config.sub: Likewise. * configure: Regenerate. config/ * mt-nanomips: New file. gas/ * Makefile.am: Add nanomips files. * Makefile.in: Regenerate. * config.in: Define a default architecture. * configure: Regenerate. * configure.ac: Likewise. * configure.tgt: Add entries for nanomips. * read.c: Likewise. * config/obj-elf.h: Likewise. * config/tc-nanomips.c: New file. * config/tc-nanomips.h: New file. ld/ * configure.tgt: Add nanomips entries. --- config/mt-nanomips | 3 + configure | 9 + configure.ac | 9 + gas/Makefile.am | 2 + gas/Makefile.in | 5 + gas/config.in | 9 + gas/config/obj-elf.h | 5 + gas/config/tc-nanomips.c | 11667 +++++++++++++++++++++++++++++++++++++ gas/config/tc-nanomips.h | 187 + gas/configure | 54 + gas/configure.ac | 45 + gas/configure.tgt | 5 + gas/read.c | 2 +- ld/configure.tgt | 2 + 14 files changed, 12003 insertions(+), 1 deletion(-) create mode 100644 config/mt-nanomips create mode 100644 gas/config/tc-nanomips.c create mode 100644 gas/config/tc-nanomips.h diff --git a/config/mt-nanomips b/config/mt-nanomips new file mode 100644 index 00000000000..48037c5001e --- /dev/null +++ b/config/mt-nanomips @@ -0,0 +1,3 @@ +# We default to building libraries optimised for size. +CFLAGS_FOR_TARGET += -Os -mno-gpopt +CXXFLAGS_FOR_TARGET += -Os -mno-gpopt diff --git a/configure b/configure index 4c2aa195810..4efb0bacc9a 100755 --- a/configure +++ b/configure @@ -4194,6 +4194,12 @@ case "${target}" in mips*-*-*) noconfigdirs="$noconfigdirs gprof" ;; + nanomips*-*-elf*) + noconfigdirs="$noconfigdirs gprof gdb sim" + ;; + nanomips*-*-linux*) + noconfigdirs="$noconfigdirs gprof gdb sim" + ;; nvptx*-*-*) noconfigdirs="$noconfigdirs target-libssp" ;; @@ -10895,6 +10901,9 @@ case "${target}" in mips*-*-*linux* | mips*-*-gnu*) target_makefile_frag="config/mt-mips-gnu" ;; + nanomips*-*-*linux*) + target_makefile_frag="config/mt-nanomips" + ;; *-*-linux-android*) target_makefile_frag="config/mt-android" ;; diff --git a/configure.ac b/configure.ac index cae0042411b..7b2481d35f4 100644 --- a/configure.ac +++ b/configure.ac @@ -1363,6 +1363,12 @@ case "${target}" in mips*-*-*) noconfigdirs="$noconfigdirs gprof" ;; + nanomips*-*-elf*) + noconfigdirs="$noconfigdirs gprof gdb sim" + ;; + nanomips*-*-linux*) + noconfigdirs="$noconfigdirs gprof gdb sim" + ;; nvptx*-*-*) noconfigdirs="$noconfigdirs target-libssp" ;; @@ -3079,6 +3085,9 @@ case "${target}" in mips*-*-*linux* | mips*-*-gnu*) target_makefile_frag="config/mt-mips-gnu" ;; + nanomips*-*-*linux*) + target_makefile_frag="config/mt-nanomips" + ;; *-*-linux-android*) target_makefile_frag="config/mt-android" ;; diff --git a/gas/Makefile.am b/gas/Makefile.am index 9f90a1de50e..e954f671eb9 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -179,6 +179,7 @@ TARGET_CPU_CFILES = \ config/tc-metag.c \ config/tc-microblaze.c \ config/tc-mips.c \ + config/tc-nanomips.c \ config/tc-mmix.c \ config/tc-mn10200.c \ config/tc-mn10300.c \ @@ -253,6 +254,7 @@ TARGET_CPU_HFILES = \ config/tc-metag.h \ config/tc-microblaze.h \ config/tc-mips.h \ + config/tc-nanomips.h \ config/tc-mmix.h \ config/tc-mn10200.h \ config/tc-mn10300.h \ diff --git a/gas/Makefile.in b/gas/Makefile.in index b2bb505cc69..ec8ee0fbd54 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -687,6 +687,7 @@ TARGET_CPU_CFILES = \ config/tc-moxie.c \ config/tc-msp430.c \ config/tc-mt.c \ + config/tc-nanomips.c \ config/tc-nds32.c \ config/tc-ns32k.c \ config/tc-or1k.c \ @@ -760,6 +761,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10300.h \ config/tc-msp430.h \ config/tc-mt.h \ + config/tc-nanomips.h \ config/tc-nds32.h \ config/tc-ns32k.h \ config/tc-or1k.h \ @@ -1187,6 +1189,8 @@ config/tc-msp430.$(OBJEXT): config/$(am__dirstamp) \ config/$(DEPDIR)/$(am__dirstamp) config/tc-mt.$(OBJEXT): config/$(am__dirstamp) \ config/$(DEPDIR)/$(am__dirstamp) +config/tc-nanomips.$(OBJEXT): config/$(am__dirstamp) \ + config/$(DEPDIR)/$(am__dirstamp) config/tc-nds32.$(OBJEXT): config/$(am__dirstamp) \ config/$(DEPDIR)/$(am__dirstamp) config/tc-ns32k.$(OBJEXT): config/$(am__dirstamp) \ @@ -1421,6 +1425,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-moxie.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-msp430.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-mt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-nanomips.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-nds32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-ns32k.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-or1k.Po@am__quote@ diff --git a/gas/config.in b/gas/config.in index ae5f41f7058..cc5a759ef84 100644 --- a/gas/config.in +++ b/gas/config.in @@ -176,6 +176,15 @@ /* Choose a default ABI for MIPS targets. */ #undef MIPS_DEFAULT_ABI +/* Default CPU for nanoMIPS targets. */ +#undef NANOMIPS_CPU_STRING_DEFAULT + +/* Generate 64-bit code by default on nanoMIPS targets. */ +#undef NANOMIPS_DEFAULT_64BIT + +/* Choose a default ABI for nanoMIPS targets. */ +#undef NANOMIPS_DEFAULT_ABI + /* Define value for nds32_arch_name */ #undef NDS32_DEFAULT_ARCH_NAME diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h index 910b743a442..c2b0308ca1c 100644 --- a/gas/config/obj-elf.h +++ b/gas/config/obj-elf.h @@ -49,6 +49,11 @@ extern int alpha_flag_mdebug; extern int mips_flag_mdebug; #endif /* TC_MIPS */ +/* Always disable ECOFF_DEBUGGING for nanoMIPS target. */ +#ifdef TC_NANOMIPS +#define ECOFF_DEBUGGING 0 +#endif /* TC_NANOMIPS */ + #ifdef OBJ_MAYBE_ECOFF #ifndef ECOFF_DEBUGGING #define ECOFF_DEBUGGING 1 diff --git a/gas/config/tc-nanomips.c b/gas/config/tc-nanomips.c new file mode 100644 index 00000000000..be2ab339a06 --- /dev/null +++ b/gas/config/tc-nanomips.c @@ -0,0 +1,11667 @@ +/* tc-nanomips.c -- assemble code for a nanoMIPS chip. + Copyright (C) 2017-2026 Free Software Foundation, CIP United Inc. + Contributed by MIPS Tech LLC. + Written by Faraz Shahbazker <[email protected]> + + 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/nanomips.h" +#include "dwarf2dbg.h" +#include "dw2gencfi.h" +#include <limits.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 (is_whitespace (*(S))) ++(S); } while (0) + +/* Clean up namespace so we can include obj-elf.h too. */ +static int nanomips_output_flavor (void); +static int +nanomips_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 nanomips_output_flavor() + +#include "elf/nanomips.h" + +#ifndef ECOFF_DEBUGGING +#define NO_ECOFF_DEBUGGING +#define ECOFF_DEBUGGING 0 +#endif + +#include "ecoff.h" + +#define ZERO 0 +#define AT 1 +#define S0 16 +#define S7 23 +#define PIC_CALL_REG 25 +#define KT0 26 +#define KT1 27 +#define GP 28 +#define SP 29 +#define FP 30 +#define RA 31 + +#define ILLEGAL_REG (32) + +extern int target_big_endian; + +/* The name of the readonly data section. */ +#define RDATA_SECTION_NAME ".rodata" + +/* Information about an instruction, including its format, operands + and fixups. */ +struct nanomips_cl_insn +{ + /* The opcode's entry in nanomips_opcodes. */ + const struct nanomips_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. */ + unsigned long insn_opcode; + + /* The lower 32-bits of a 48-bit instruction. */ + unsigned long insn_opcode_ext; + + /* 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 if this instruction is complete. */ + unsigned int complete_p:1; +}; + +/* The ABI to use. */ +enum nanomips_abi_level +{ + NO_ABI = 0, + P32_ABI, + P64_ABI, +}; + +/* The ABI to use. */ +enum mc_model_type +{ + MC_AUTO = 0, + MC_MEDIUM, + MC_LARGE, +}; + +/* nanoMIPS ABI we are using for this output file. */ +static enum nanomips_abi_level nanomips_abi = NO_ABI; + +/* nanoMIPS PIC level. */ + +enum nanomips_pic_level +{ + /* Do not generate PIC code. */ + NO_PIC, + + /* Generate medium model PIC code as in the nanoMIPS ABI. */ + SVR4_PIC, + + /* Generate large model PIC code as in the nanoMIPS ABI. */ + SVR4_LARGE_PIC, +}; + +/* 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 nanomips_set_options +{ + /* nanoMIPS ISA (Instruction Set Architecture) level. This is set to -1 + if it has not been initialized. */ + int isa; + + /* Enabled Application Specific Extensions (ASEs). Changed by `.set + <asename>', by command line options, and based on the default + architecture. */ + int ase; + + /* 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 $at ($1) register to be used + in instructions. Changed by `.set at' and `.set noat'. */ + int noat; + + /* 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 nomacro; + + /* True if we should only emit 32-bit nanoMIPS 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. */ + int gp; + int fp; + + /* nanoMIPS architecture (CPU) type. Changed by .set arch=FOO, the -march + command line option, and the default CPU. */ + int arch; + + /* 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; + + /* 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; + + /* Enable/disable balc stub optimization. */ + bool no_balc_stubs; + + /* Enable/disable register names, only for nanoMIPS. */ + bool legacyregs; + + /* Enable/disable PC-relative expansion. */ + bool pcrel; + + /* Enable/disable Position Independent data. */ + bool pid; + + /* Select PIC code. */ + enum nanomips_pic_level pic; + + /* Code/data memory model. */ + enum mc_model_type mc_model; +}; + +/* Specifies whether module level options have been checked yet. */ +static bool file_nanomips_opts_checked = false; + +/* 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 nanomips_set_options file_nanomips_opts = { + /* isa */ ISA_UNKNOWN, /* ase */ 0, /* noreorder */ 0, /* at */ 0, + /* nomacro */ 0, /* insn32 */ false, /* gp */ -1, /* fp */ -1, + /* arch */ CPU_UNKNOWN, /* soft_float */ false, /* single_float */ false, + /* init_ase */ 0, /* no_balc_stubs */ true, /* legacyregs */ false, + /* pcrel */ false, /* pid */ false, /* pic */ NO_PIC, + /* mc_model */ MC_AUTO +}; + +/* This is similar to file_nanomips_opts, but for the current set of options. */ + +static struct nanomips_set_options nanomips_opts = { + /* isa */ ISA_UNKNOWN, /* ase */ 0, /* noreorder */ 0, /* at */ 0, + /* nomacro */ 0, /* insn32 */ false, /* gp */ -1, /* fp */ -1, + /* arch */ CPU_UNKNOWN, /* soft_float */ false, /* single_float */ false, + /* init_ase */ 0, /* no_balc_stubs */ true, /* legacyregs */ false, + /* pcrel */ false, /* pid */ false, /* pic */ NO_PIC, + /* mc_model */ MC_AUTO +}; + +/* Which bits of file_ase were explicitly set or cleared by ASE options. */ +static unsigned int file_ase_explicit; + +/* The argument of the -march= flag. The architecture we are assembling. */ +static const char *nanomips_arch_string; + +/* The argument of the -mtune= flag. The architecture for which we + are optimizing. */ +static int nanomips_tune = CPU_UNKNOWN; +static const char *nanomips_tune_string; + +/* True when generating 32-bit code for a 64-bit processor. */ +static int nanomips_32bitmode = 0; + +/* True if the given ABI requires 32-bit registers. */ +#define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == P32_ABI) + +/* Likewise 64-bit registers. */ +#define ABI_NEEDS_64BIT_REGS(ABI) ((ABI) == P64_ABI) + +/* Return true if ISA supports 64 bit wide gp registers. */ +#define ISA_HAS_64BIT_REGS(ISA) ((ISA) == ISA_NANOMIPS64R6) + +#define GPR_SIZE nanomips_opts.gp + +#define HAVE_64BIT_OBJECTS (nanomips_abi == P64_ABI) + +/* The ABI-derived address size. */ +#define HAVE_64BIT_ADDRESSES (GPR_SIZE == 64 && nanomips_abi == P64_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) +#define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS) + +/* Maximum symbol offset that can be encoded in a BFD_RELOC_GPREL16 + relocation. */ +#define MAX_GPREL_OFFSET (0x1fffc) + +/* Addresses are loaded in different ways, depending on the address size + in use. */ +#define ADDRESS_ADD_INSN "addu" + +#define ADDRESS_ADDI_INSN "addiu" + +#define ADDRESS_LOAD_INSN "lw" + +#define ADDRESS_STORE_INSN "sw" + +#define PCREL_LOAD_INSN "lwpc" + +/* 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) + +/* 1 if trap instructions should used for overflow rather than break + instructions. */ +static int nanomips_trap = 0; + +/* 1 if double width floating point constants should not be constructed + by assembling two single width halves within the instruction stream, + but instead loaded from memory. */ +static int nanomips_disable_float_construction; + +/* Non-zero if .set [no]relax directive was used */ +static bool toggle_linkrelax_p = false; +static bool nanomips_linkrelax_p = false; + +/* 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; + +/* 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 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. */ +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 nanoMIPS ASE. */ +static htab_t nanomips_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 nanomips_insn_error_format +{ + ERR_FMT_PLAIN, + ERR_FMT_I, + ERR_FMT_SS, + ERR_FMT_SI, +}; + +/* Information about an error that was found while assembling the current + instruction. */ +struct nanomips_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 nanomips_insn_error_format format; + const char *msg; + union + { + int i; + const char *ss[2]; + struct + { + const char *s1; + int u1; + } si; + } u; +}; + +/* The error that should be reported for the current instruction. */ +static struct nanomips_insn_error insn_error; + +static int auto_align = 1; + +static int nanomips_gp_register = GP; + +/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is + equivalent to seeing no -g option at all. */ +static int nanomips_debug = 0; + +/* 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 to + fill a branch delay slot. */ +static struct nanomips_cl_insn history; + +/* Arrays of operands for each instruction. */ +#define MAX_OPERANDS 6 +struct nanomips_operand_array +{ + const struct nanomips_operand *operand[MAX_OPERANDS]; +}; +static struct nanomips_operand_array *nanomips_operands; + +/* Nop instructions used by emit_nop. */ +static struct nanomips_cl_insn nanomips_nop16_insn; +static struct nanomips_cl_insn nanomips_nop32_insn; + +/* The appropriate nop for the current mode. */ +#define NOP_INSN (nanomips_opts.insn32 \ + ? &nanomips_nop32_insn \ + : &nanomips_nop16_insn) + +/* The size of NOP_INSN in bytes. */ +#define NOP_INSN_SIZE (nanomips_opts.insn32? 4 : 2) + +/* 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; + +/* Map 3-bit register numbers to normal nanoMIPS register numbers. */ + +static const unsigned int nanomips_to_32_reg_d_map[] = { + 16, 17, 18, 19, 4, 5, 6, 7 +}; + +/* 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_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. + + 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) (((FIRST) << 8) | (SECOND)) + +#define RELAX_FIRST(X) (((X) >> 8) & 0xff) +#define RELAX_SECOND(X) ((X) & 0xff) +#define RELAX_USE_SECOND 0x10000 +#define RELAX_SECOND_LONGER 0x20000 +#define RELAX_NOMACRO 0x40000 + +/* For nanoMIPS code, we use relaxation similar to one we use for + microMIPS 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. + + There are no delayed branches in nanoMIPS and we do not relax + 32-bit branch instructions that do not fit within 32-bit range at + assembly, so attributes related to those features are removed. + Instead we relax 32-bit calls that do not fit within 16-bit range + in to calls to 32-bit unconditional branch stubs which can fit within + 16-bit range. + + USESTUB and ONCESTUB track calls which can be relaxed to stubs. KEEPSTUB + tracks stubs that have sufficient calls (at least 3) against them to be + instantiated for reducing code size. + + TOOFAR16/NEGOFF are for relaxation of the ADDIU opcode in to + 32/NEG/R2/RS5 variants. + !TOOFAR16 && NEGOFF => ADDIU[RS5], consequent to operand constraints + !TOOFAR16 && !NEGOFF => ADDIU[R2], consequent to operand constraints + TOOFAR16 && NEGOFF => ADDIU[NEG] + TOOFAR16 && !NEGOFF => ADDIU[32] +*/ + +enum +{ + RT_ADDIU = 1, + RT_BRANCH_UCND, + RT_BRANCH_CNDZ, + RT_BRANCH_CND, + RT_BALC_STUB, +}; + +#define RELAX_MD_ENCODE(type, link, ext) \ + (0x20000000 \ + | ((type) & 0xff) \ + | ((ext) ? 0x100 : 0) \ + | ((link) ? 0x2000 : 0)) +#define RELAX_MD_P(i) (((i) & 0xe0000000) == 0x20000000) +#define RELAX_MD_TYPE(i) ((i) & 0xff) +#define RELAX_MD_LINK(i) (((i) & 0x100) != 0) + +#define RELAX_MD_BALC_STUB_P(i) \ + (RELAX_MD_P (i) && ((i) & 0xff) == RT_BALC_STUB) + +#define RELAX_MD_BRANCH_P(i) \ + (RELAX_MD_P (i) && (((i) & 0xff) == RT_BRANCH_UCND \ + || ((i) & 0xff) == RT_BRANCH_CNDZ \ + || ((i) & 0xff) == RT_BRANCH_CND)) + +#define RELAX_MD_ADDIU_P(i) \ + (RELAX_MD_P (i) && ((i) & 0xff) == RT_ADDIU) + +#define RELAX_MD_KEEPSTUB(i) (((i) & 0x200) != 0) +#define RELAX_MD_MARK_KEEPSTUB(i) ((i) | 0x200) +#define RELAX_MD_CLEAR_KEEPSTUB(i) ((i) & ~0x200) + +#define RELAX_MD_USESTUB(i) (((i) & 0x400) != 0) +#define RELAX_MD_MARK_USESTUB(i) ((i) | 0x400) +#define RELAX_MD_CLEAR_USESTUB(i) ((i) & ~0x400) + +#define RELAX_MD_ONCESTUB(i) (((i) & 0x800) != 0) +#define RELAX_MD_MARK_ONCESTUB(i) ((i) | 0x800) +#define RELAX_MD_CLEAR_ONCESTUB(i) ((i) & ~0x800) + +#define RELAX_MD_TOOFAR16(i) (((i) & 0x1000) != 0) +#define RELAX_MD_MARK_TOOFAR16(i) ((i) | 0x1000) +#define RELAX_MD_CLEAR_TOOFAR16(i) ((i) & ~0x1000) + +#define RELAX_MD_TOOFAR32(i) (((i) & 0x2000) != 0) +#define RELAX_MD_MARK_TOOFAR32(i) ((i) | 0x2000) +#define RELAX_MD_CLEAR_TOOFAR32(i) ((i) & ~0x2000) + +#define RELAX_MD_NEGOFF(i) (((i) & 0x4000) != 0) +#define RELAX_MD_MARK_NEGOFF(i) ((i) | 0x4000) +#define RELAX_MD_CLEAR_NEGOFF(i) ((i) & ~0x4000) + +#define RELAX_MD_FIXED(i) (((i) & 0x1000) != 0) + +/* 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 an unsigned 16-bit value? */ +#define IS_SEXT_16BIT_UINT(x) (((x) &~ (offsetT) 0xffff) == 0) + +/* 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)) + +/* 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; +} nanomips_relax; + +/* Global variables used to decide whether a macro needs a warning. */ +static struct +{ + /* 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; +} nanomips_macro_warning; + +/* Prototypes for static functions. */ + +enum nanomips_regclass + { NANOMIPS_GR_REG, NANOMIPS_FP_REG }; + +static void append_insn + (struct nanomips_cl_insn *, expressionS *, bfd_reloc_code_real_type *, + bool expansionp); +static void macro_build (expressionS *, const char *, const char *, ...); +static void load_register (int, expressionS *, int); +static void macro_start (void); +static void macro_end (bool); +static void macro (struct nanomips_cl_insn *ip, char *str); +static void nanomips_ip (char *str, struct nanomips_cl_insn *ip); +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_nanomipsset (int); +static void s_cpsetup (int); +static void s_dtprelword (int); +static void s_dtpreldword (int); +static void s_ehword (int); +static void s_insn (int); +static void s_module (int); +static void s_nanomips_ent (int); +static void s_nanomips_end (int); +static void s_nanomips_frame (int); +static void s_nanomips_mask (int reg_type); +static void s_nanomips_file (int); +static void s_nanomips_loc (int); +static void s_linkrelax (int); +static bool pic_need_relax (symbolS *, asection *); +static void file_check_options (void); +static void stubgroup_new (asection *); +static void s_sign_cons (int); +static bool nanomips_allow_local_subtract_symbols (symbolS *, + symbolS *, + bool); + +/* Table and functions used to map between CPU/ISA names, and + ISA levels, and CPU numbers. */ + +struct nanomips_cpu_info +{ + const char *name; /* CPU or ISA name. */ + int flags; /* NANOMIPS_CPU_* flags. */ + int ase; /* Set of ASEs implemented by the CPU. */ + int isa; /* ISA level. */ + int cpu; /* CPU number (default CPU if ISA). */ +}; + +#define NANOMIPS_CPU_IS_ISA 0x0001 /* Is this an ISA? (If 0, a CPU.) */ + +static const struct nanomips_cpu_info *nanomips_parse_cpu + (const char *, const char *); +static const struct nanomips_cpu_info *nanomips_cpu_info_from_isa + (int, bool); +static const struct nanomips_cpu_info *nanomips_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_NO_MIPS16, + OPTION_MT, + OPTION_NO_MT, + OPTION_VIRT, + OPTION_NO_VIRT, + OPTION_NO_MICROMIPS, + OPTION_TRAP, + OPTION_BREAK, + OPTION_EB, + OPTION_EL, + OPTION_CONSTRUCT_FLOATS, + OPTION_NO_CONSTRUCT_FLOATS, + OPTION_INSN32, + OPTION_NO_INSN32, + OPTION_SOFT_FLOAT, + OPTION_HARD_FLOAT, + OPTION_SINGLE_FLOAT, + OPTION_DOUBLE_FLOAT, + OPTION_32, + OPTION_64, + OPTION_M32, + OPTION_M64, + OPTION_BALC_STUBS, + OPTION_NO_BALC_STUBS, + OPTION_LEGACY_REGS, + OPTION_NO_LEGACY_REGS, + OPTION_LINKRELAX, + OPTION_PCREL, + OPTION_NO_PCREL, + OPTION_PID, + OPTION_NO_PID, + OPTION_PIC, + OPTION_NOPIC, + OPTION_LARGE_PIC, + OPTION_MCMODEL, + OPTION_END_OF_ENUM +}; + +const struct option md_longopts[] = +{ + /* Options which specify architecture. */ + {"march", required_argument, NULL, OPTION_MARCH}, + {"mtune", required_argument, NULL, OPTION_MTUNE}, + + /* Options which specify Application Specific Extensions (ASEs). */ + {"mmt", no_argument, NULL, OPTION_MT}, + {"mno-mt", no_argument, NULL, OPTION_NO_MT}, + {"mvirt", no_argument, NULL, OPTION_VIRT}, + {"mno-virt", no_argument, NULL, OPTION_NO_VIRT}, + + /* Miscellaneous options. */ + {"32", no_argument, NULL, OPTION_32}, + {"64", no_argument, NULL, OPTION_64}, + {"m32", no_argument, NULL, OPTION_M32}, + {"m64", no_argument, NULL, OPTION_M64}, + {"EB", no_argument, NULL, OPTION_EB}, + {"EL", no_argument, NULL, OPTION_EL}, + {"mcmodel", required_argument, NULL, OPTION_MCMODEL}, + {"mpic", no_argument, NULL, OPTION_PIC}, + {"mno-pic", no_argument, NULL, OPTION_NOPIC}, + {"mPIC", no_argument, NULL, OPTION_LARGE_PIC}, + {"mno-PIC", no_argument, NULL, OPTION_NOPIC}, + {"mpid", no_argument, NULL, OPTION_PID}, + {"mno-pid", no_argument, NULL, OPTION_NO_PID}, + {"mpcrel", no_argument, NULL, OPTION_PCREL}, + {"mno-pcrel", no_argument, NULL, OPTION_NO_PCREL}, + {"minsn32", no_argument, NULL, OPTION_INSN32}, + {"mno-insn32", no_argument, NULL, OPTION_NO_INSN32}, + {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT}, + {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT}, + {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT}, + {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT}, + {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS}, + {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS}, + {"mbalc-stubs", no_argument, NULL, OPTION_BALC_STUBS}, + {"mno-balc-stubs", no_argument, NULL, OPTION_NO_BALC_STUBS}, + {"mlegacyregs", no_argument, NULL, OPTION_LEGACY_REGS}, + {"mno-legacyregs", no_argument, NULL, OPTION_NO_LEGACY_REGS}, + {"linkrelax", no_argument, NULL, OPTION_LINKRELAX}, + {"break", no_argument, NULL, OPTION_BREAK}, + {"no-break", no_argument, NULL, OPTION_TRAP}, + {"trap", no_argument, NULL, OPTION_TRAP}, + {"no-trap", no_argument, NULL, OPTION_BREAK}, + + {NULL, no_argument, NULL, 0} +}; + +const 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 nanomips_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; + + int nanomips32_rev; + int nanomips64_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 nanomips_ase nanomips_ases[] = { + {"mt", ASE_MT, 0, + OPTION_MT, OPTION_NO_MT, + 6, 6, + -1}, + + {"virt", ASE_VIRT, ASE_VIRT64, + OPTION_VIRT, OPTION_NO_VIRT, + 6, 6, + -1}, +}; + +/* Pseudo-op table. */ + +static const pseudo_typeS nanomips_pseudo_table[] = { + /* nanoMIPS specific pseudo-ops. */ + {"set", s_nanomipsset, 0}, + {"rdata", s_change_sec, 'r'}, + {"sdata", s_change_sec, 's'}, + {"cpload", s_ignore, 0}, + {"cpsetup", s_cpsetup, 0}, + {"cplocal", s_ignore, 0}, + {"cprestore", s_ignore, 0}, + {"cpreturn", s_ignore, 0}, + {"dtprelword", s_dtprelword, 0}, + {"dtpreldword", s_dtpreldword, 0}, + {"ehword", s_ehword, 0}, + {"cpadd", s_ignore, 0}, + {"insn", s_insn, 0}, + {"module", s_module, 0}, + + /* Relatively generic pseudo-ops that happen to be used on MIPS. */ + {"bss", s_change_sec, 'b'}, + {"err", s_err, 0}, + {"half", s_cons, 1}, + {"dword", s_cons, 3}, + {"origin", s_org, 0}, + {"repeat", s_rept, 0}, + + /* 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'}, + {"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'}, + {"text", s_change_sec, 't'}, + {"word", s_cons, 2}, + {"uleb128", s_leb128, 0}, + {"sleb128", s_leb128, 1}, + + {"extern", ecoff_directive_extern, 0}, + + /* These psuedo-ops are specific to nanoMIPS relocatable expression + for jump offset tables. */ + {"sbyte", s_sign_cons, 0}, + {"shword", s_sign_cons, 1}, + + /* Control linker-relaxation for nanoMIPS */ + {"linkrelax", s_linkrelax, 0}, + + {NULL, NULL, 0}, +}; + +static const pseudo_typeS nanomips_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_nanomips_ent, 1}, + {"bgnb", s_ignore, 0}, + {"end", s_nanomips_end, 0}, + {"endb", s_ignore, 0}, + {"ent", s_nanomips_ent, 0}, + {"file", s_nanomips_file, 0}, + {"fmask", s_nanomips_mask, 'F'}, + {"frame", s_nanomips_frame, 0}, + {"loc", s_nanomips_loc, 0}, + {"mask", s_nanomips_mask, 'R'}, + {"verstamp", s_ignore, 0}, + {NULL, NULL, 0}, +}; + +/* Data structures for BALC stub optimization for nanoMIPS ISA. */ +/* List of call-sites to be resolved by a stub. */ +struct call_list +{ + bfd_vma callsite; + struct call_list *next; +}; + +/* Tracking info for each planned stub. */ +struct balc_stub +{ + unsigned int numcalls; + struct call_list *first_call; + struct call_list *last_call; + fragS *fragp; + symbolS *sym; +}; + +/* Doubly linked list of all planned stubs groups. */ +typedef struct stub_group_s +{ + htab_t stubtable; + bfd_vma next_offset; + fragS *fragp; + struct stub_group_s *prev; + struct stub_group_s *next; + asection *seg; +} stub_group; + +/* Master-table of all stubgroups by section-name. */ +static htab_t balc_stubgroup_table = NULL; +/* Quick pointer to the stubgroup currently under consideration + for relaxation. */ +static stub_group *stubg_now = NULL; +/* Flag to check when we are in function-less mode. */ +static bool stub_funcless_mode = false; +/* BC32 to be used as a call-stub. */ +static struct nanomips_cl_insn nanomips_bc32_insn; +typedef struct proc +{ + symbolS *func_sym; + symbolS *func_end_sym; + unsigned long reg_mask; + unsigned long reg_offset; + unsigned long fpreg_mask; + unsigned long fpreg_offset; + unsigned long frame_offset; + unsigned long frame_reg; + unsigned long pc_reg; +} procS; +static procS *cur_proc_ptr; + +/* Tracking state for signed cons expression. */ +static bool sign_cons = false; + +/* Local and reversed copy of the explicit relocation list. */ +static struct reloc_list *reloc_list_copy; +/* Last explicit relocation pointer matched to a frag. */ +static struct reloc_list *reloc_list_iter; +/* Whether the order of explicit relocations in the source match + the order of the corresponding offset symbols. The order is + typically guaranteed by how the compiler generates explicit + relocations, we provide a fail-safe for handwritten assembly. */ +bool reloc_ordered = true; + +/* Export the ABI address size for use by TC_ADDRESS_BYTES for the + purpose of the `.dc.a' internal pseudo-op. */ + +int +nanomips_address_bytes (void) +{ + file_check_options (); + return HAVE_64BIT_ADDRESSES ? 8 : 4; +} + +void +nanomips_pop_insert (void) +{ + pop_insert (nanomips_pseudo_table); + if (!ECOFF_DEBUGGING) + pop_insert (nanomips_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 + +static void nanomips_clear_insn_labels (void); + +static inline void +nanomips_clear_insn_labels (void) +{ + register 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; + } +} + +static char *expr_parse_end; + +/* An expression in a macro instruction. This is set by nanomips_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 nanomips_ip if an explicit size is supplied. */ + +static unsigned int forced_insn_length; + +/* This is set if the mnemonic includes a specific [xx] format + notation from the architecture reference manual. */ + +static bool forced_insn_format; + +/* The default target format to use. */ + +#if defined (TE_FreeBSD) +#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips-freebsd" +#elif defined (TE_TMIPS) +#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "nanomips" +#else +#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips" +#endif + +#define ELF_NTARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips" + +const char * +nanomips_target_format (void) +{ + switch (OUTPUT_FLAVOR) + { + case bfd_target_elf_flavour: + return (target_big_endian + ? (HAVE_64BIT_OBJECTS + ? ELF_NTARGET ("elf64-", "big") + : ELF_NTARGET ("elf32-", "big")) + : (HAVE_64BIT_OBJECTS + ? ELF_NTARGET ("elf64-", "little") + : ELF_NTARGET ("elf32-", "little"))); + default: + abort (); + return NULL; + } +} + +/* Return the ISA revision that is currently in use. */ + +static inline int +nanomips_isa_rev (void) +{ + return 6; +} + +/* Check whether the current ISA supports ASE. Issue a warning if + appropriate. */ + +static void +check_isa_supports_ase (const struct nanomips_ase *ase) +{ + int min_rev, size; + + if (ISA_HAS_64BIT_REGS (nanomips_opts.isa)) + min_rev = ase->nanomips64_rev; + else + min_rev = ase->nanomips32_rev; + + if (min_rev < 0 || nanomips_isa_rev () < min_rev) + { + size = ISA_HAS_64BIT_REGS (nanomips_opts.isa) ? 64 : 32; + if (min_rev < 0) + as_warn (_("the %d-bit nanoMIPS architecture does not support the" + " `%s' extension"), size, ase->name); + else + as_warn (_("the `%s' extension requires nanoMIPS%d revision %d or " + "greater"), ase->name, size, min_rev); + } +} + +/* Check all enabled ASEs to see whether they are supported by the + chosen architecture. */ + +static void +check_isa_supports_ases (void) +{ + unsigned int i, mask; + + for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++) + { + mask = nanomips_ases[i].flags; + if ((nanomips_opts.ase & mask) == nanomips_ases[i].flags) + check_isa_supports_ase (&nanomips_ases[i]); + } +} + +/* Set the state of ASE to ENABLED_P. Return the mask of ASE_* flags + that were affected. */ + +static unsigned int +nanomips_set_ase (const struct nanomips_ase *ase, + struct nanomips_set_options *opts, bool enabled_p) +{ + unsigned int mask; + + mask = ase->flags; + opts->ase &= ~mask; + + if (enabled_p) + opts->ase |= ase->flags; + + return mask; +} + +/* Return the ASE called NAME, or null if none. */ + +static const struct nanomips_ase * +nanomips_lookup_ase (const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++) + if (strcmp (name, nanomips_ases[i].name) == 0) + return &nanomips_ases[i]; + return NULL; +} + +/* Return the length of a nanoMIPS instruction in bytes. */ + +static inline unsigned int +insn_length (const struct nanomips_opcode *mo) +{ + return (opcode_48bit_p (mo) ? 6 : (nanomips_opcode_32bit_p (mo) ? 4 : 2)); +} + +/* Initialise INSN from opcode entry MO. Leave its position unspecified. */ + +static void +create_insn (struct nanomips_cl_insn *insn, const struct nanomips_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 = (nanomips_opts.noreorder > 0); + insn->noreorder_p = (nanomips_opts.noreorder > 0); + insn->complete_p = 0; +} + +/* Get a list of all the operands in INSN. */ + +static const struct nanomips_operand_array * +insn_operands (const struct nanomips_cl_insn *insn) +{ + if (insn->insn_mo >= &nanomips_opcodes[0] + && insn->insn_mo < &nanomips_opcodes[bfd_nanomips_num_opcodes]) + return &nanomips_operands[insn->insn_mo - &nanomips_opcodes[0]]; + + abort (); +} + +/* Install UVAL as the value of OPERAND in INSN. */ + +static inline void +insn_insert_operand (struct nanomips_cl_insn *insn, + const struct nanomips_operand *operand, + unsigned int uval) +{ + insn->insn_opcode = nanomips_insert_operand (operand, insn->insn_opcode, + uval); +} + +/* Extract the value of OPERAND from INSN. */ + +static inline unsigned +insn_extract_operand (const struct nanomips_cl_insn *insn, + const struct nanomips_operand *operand) +{ + return nanomips_extract_operand (operand, insn->insn_opcode); +} + +/* Read a compressed instruction of 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 compressed 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 nanomips_cl_insn *insn) +{ + char *f = insn->frag->fr_literal + insn->where; + + if (insn_length (insn->insn_mo) == 6) + { + write_compressed_insn (f, insn->insn_opcode, 2); + write_compressed_insn (f + 2, insn->insn_opcode_ext, 4); + } + else + write_compressed_insn (f, insn->insn_opcode, insn_length (insn->insn_mo)); +} + +static void +toggle_linkrelax (fragS *frag, long where) +{ + nanomips_linkrelax_p = !nanomips_linkrelax_p; + toggle_linkrelax_p = false; + fix_new (frag, where, 0, &abs_symbol, 0, false, + (nanomips_linkrelax_p + ? BFD_RELOC_NANOMIPS_RELAX + : BFD_RELOC_NANOMIPS_NORELAX)); +} + +/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly + and install the opcode in the new location. */ + +static void +move_insn (struct nanomips_cl_insn *insn, fragS *frag, long where) +{ + size_t i; + + insn->frag = frag; + insn->where = where; + + if (toggle_linkrelax_p) + toggle_linkrelax (frag, 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 nanomips_cl_insn *insn) +{ + char *f = frag_more (insn_length (insn->insn_mo)); + 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 nanomips_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 (const struct nanomips_cl_insn *insn) +{ + if (nanomips_relax.sequence != 2) + history = *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 nanomips_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 nanomips_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; + } +} + +static void +set_insn_error_si (int argnum, const char *msg, const char *s1, const int i) +{ + if (set_insn_error_format (argnum, ERR_FMT_SI, msg)) + { + insn_error.u.si.s1 = s1; + insn_error.u.si.u1 = i; + } +} + +/* 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'", (const char *) 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; + + case ERR_FMT_SI: + as_bad (msg, insn_error.u.si.s1, insn_error.u.si.u1, str); + break; + } +} + +struct regname +{ + const char *name; + unsigned int num; +}; + +#define RNUM_MASK 0x00003ff +#define RTYPE_MASK 0x7fffc00 +#define RTYPE_NUM 0x0000400 +#define RTYPE_FPU 0x0000800 +#define RTYPE_FCC 0x0001000 +#define RTYPE_VEC 0x0002000 +#define RTYPE_GP 0x0004000 +#define RTYPE_CP0 0x0008000 +#define RTYPE_PC 0x0010000 +#define RTYPE_ACC 0x0020000 +#define RTYPE_CCC 0x0040000 +#define RTYPE_CP0SEL 0x0300000 +#define RTYPE_CP0SEL_EVEN 0x0100000 +#define RTYPE_CP0SEL_ODD 0x0200000 +#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 NANOMIPS_SYMBOLIC_REG_NAMES \ + {"$zero", RTYPE_GP | 0}, \ + {"$at", RTYPE_GP | 1}, \ + {"$t4", RTYPE_GP | 2}, \ + {"$t5", RTYPE_GP | 3}, \ + {"$a0", RTYPE_GP | 4}, \ + {"$a1", RTYPE_GP | 5}, \ + {"$a2", RTYPE_GP | 6}, \ + {"$a3", RTYPE_GP | 7}, \ + {"$a4", RTYPE_GP | 8}, \ + {"$a5", RTYPE_GP | 9}, \ + {"$a6", RTYPE_GP | 10}, \ + {"$a7", RTYPE_GP | 11}, \ + {"$t0", RTYPE_GP | 12}, \ + {"$t1", RTYPE_GP | 13}, \ + {"$t2", RTYPE_GP | 14}, \ + {"$t3", RTYPE_GP | 15}, \ + {"$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}, \ + {"$k1", RTYPE_GP | 27}, \ + {"$gp", RTYPE_GP | 28}, \ + {"$sp", RTYPE_GP | 29}, \ + {"$fp", RTYPE_GP | 30}, \ + {"$ra", RTYPE_GP | 31}, \ + {"$AT", RTYPE_GP | 1} + +#define NANOMIPS_NUMERIC_REG_NAMES \ + {"$r0", RTYPE_GP | 0}, \ + {"$r1", RTYPE_GP | 1}, \ + {"$r2", RTYPE_GP | 2}, \ + {"$r3", RTYPE_GP | 3}, \ + {"$r4", RTYPE_GP | 4}, \ + {"$r5", RTYPE_GP | 5}, \ + {"$r6", RTYPE_GP | 6}, \ + {"$r7", RTYPE_GP | 7}, \ + {"$r8", RTYPE_GP | 8}, \ + {"$r9", RTYPE_GP | 9}, \ + {"$r10", RTYPE_GP | 10}, \ + {"$r11", RTYPE_GP | 11}, \ + {"$r12", RTYPE_GP | 12}, \ + {"$r13", RTYPE_GP | 13}, \ + {"$r14", RTYPE_GP | 14}, \ + {"$r15", RTYPE_GP | 15}, \ + {"$r16", RTYPE_GP | 16}, \ + {"$r17", RTYPE_GP | 17}, \ + {"$r18", RTYPE_GP | 18}, \ + {"$r19", RTYPE_GP | 19}, \ + {"$r20", RTYPE_GP | 20}, \ + {"$r21", RTYPE_GP | 21}, \ + {"$r22", RTYPE_GP | 22}, \ + {"$r23", RTYPE_GP | 23}, \ + {"$r24", RTYPE_GP | 24}, \ + {"$r25", RTYPE_GP | 25}, \ + {"$r26", RTYPE_GP | 26}, \ + {"$r27", RTYPE_GP | 27}, \ + {"$r28", RTYPE_GP | 28}, \ + {"$r29", RTYPE_GP | 29}, \ + {"$r30", RTYPE_GP | 30}, \ + {"$r31", RTYPE_GP | 31} + +#define NANOMIPS_DSP_ACCUMULATOR_NAMES \ + {"$ac0", RTYPE_ACC | 0}, \ + {"$ac1", RTYPE_ACC | 1}, \ + {"$ac2", RTYPE_ACC | 2}, \ + {"$ac3", RTYPE_ACC | 3} + +static const struct regname nanomips_reg_names[] = { + GENERIC_REGISTER_NUMBERS, + NANOMIPS_SYMBOLIC_REG_NAMES, + NANOMIPS_NUMERIC_REG_NAMES, + FPU_REGISTER_NAMES, + FPU_CONDITION_CODE_NAMES, + COPROC_CONDITION_CODE_NAMES, + NANOMIPS_DSP_ACCUMULATOR_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 +nanomips_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 +nanomips_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. + + When returning true, move *SPTR past the register, store the + register's symbol value in *SYMVAL_PTR. */ + +static bool +nanomips_parse_register (char **sptr, unsigned int *symval_ptr) +{ + char *s, *e; + unsigned int symval; + + /* Find end of name. */ + s = e = *sptr; + if (is_name_beginner (*e)) + ++e; + while (is_part_of_name (*e)) + ++e; + + if (!nanomips_parse_register_1 (s, e, &symval)) + return false; + + *sptr = e; + *symval_ptr = symval; + 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 (nanomips_parse_register (s, ®no)) + { + if (types & RTYPE_VEC) + regno = nanomips_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; +} + +/* Token types for parsed operand lists. */ +enum nanomips_operand_token_type +{ + /* A plain register, e.g. $f2. */ + OT_REG, + + /* A constant vector index, e.g. [1]. */ + OT_INTEGER_INDEX, + + /* A register vector index, e.g. [$2]. */ + OT_REG_INDEX, + + /* A continuous range of registers, e.g. $s0-$s4. */ + OT_REG_RANGE, + + /* A (possibly relocated) expression. */ + OT_INTEGER, + + /* A floating-point value. */ + OT_FLOAT, + + /* A single character. This can be '(', ')' or ',', but '(' only appears + before OT_REGs. */ + OT_CHAR, + + /* A doubled character, either "--" or "++". */ + OT_DOUBLE_CHAR, + + /* The end of the operand list. */ + OT_END +}; + +/* A parsed operand token. */ +struct nanomips_operand_token +{ + /* The type of token. */ + enum nanomips_operand_token_type type; + union + { + /* The register symbol value for an OT_REG or OT_REG_INDEX. */ + unsigned int regno; + + /* The integer value of an OT_INTEGER_INDEX. */ + addressT index; + + /* The two register symbol values involved in an OT_REG_RANGE. */ + struct + { + unsigned int regno1; + unsigned int regno2; + } reg_range; + + /* The value of an OT_INTEGER. The value is represented as an + expression and the relocation operators that were applied to + that expression. The reloc entries are BFD_RELOC_UNUSED if no + relocation operators were used. */ + struct + { + expressionS value; + bfd_reloc_code_real_type relocs[3]; + } integer; + + /* The binary data for an OT_FLOAT constant, and the number of bytes + in the constant. */ + struct + { + unsigned char data[8]; + int length; + } flt; + + /* The character represented by an OT_CHAR or OT_DOUBLE_CHAR. */ + char ch; + } u; +}; + +/* An obstack used to construct lists of nanomips_operand_tokens. */ +static struct obstack nanomips_operand_tokens; + +/* Give TOKEN type TYPE and add it to nanomips_operand_tokens. */ + +static void +nanomips_add_token (struct nanomips_operand_token *token, + enum nanomips_operand_token_type type) +{ + token->type = type; + obstack_grow (&nanomips_operand_tokens, token, sizeof (*token)); +} + +/* Check whether S is '(' followed by a register name. Add OT_CHAR + and OT_REG tokens for them if so, and return a pointer to the first + unconsumed character. Return null otherwise. */ + +static char * +nanomips_parse_base_start (char *s) +{ + struct nanomips_operand_token token; + unsigned int regno; + bool decrement_p; + + if (*s != '(') + return 0; + + ++s; + SKIP_SPACE_TABS (s); + + /* Only match "--" as part of a base expression. In other contexts "--X" + is a double negative. */ + decrement_p = (s[0] == '-' && s[1] == '-'); + if (decrement_p) + { + s += 2; + SKIP_SPACE_TABS (s); + } + + /* Allow a channel specifier because that leads to better error messages + than treating something like "$vf0x++" as an expression. */ + if (!nanomips_parse_register (&s, ®no)) + return 0; + + token.u.ch = '('; + nanomips_add_token (&token, OT_CHAR); + + if (decrement_p) + { + token.u.ch = '-'; + nanomips_add_token (&token, OT_DOUBLE_CHAR); + } + + token.u.regno = regno; + nanomips_add_token (&token, OT_REG); + + /* For consistency, only match "++" as part of base expressions too. */ + SKIP_SPACE_TABS (s); + if (s[0] == '+' && s[1] == '+') + { + s += 2; + token.u.ch = '+'; + nanomips_add_token (&token, OT_DOUBLE_CHAR); + } + return s; +} + +/* Parse one or more tokens from S. Return a pointer to the first + unconsumed character on success. Return null if an error was found + and store the error text in insn_error. FLOAT_FORMAT is as for + nanomips_parse_arguments. */ + +static char * +nanomips_parse_argument_token (char *s, char float_format) +{ + char *end, *save_in; + const char *err; + unsigned int regno1, regno2; + struct nanomips_operand_token token; + + /* First look for "($reg", since we want to treat that as an + OT_CHAR and OT_REG rather than an expression. */ + end = nanomips_parse_base_start (s); + if (end) + return end; + + /* Handle other characters that end up as OT_CHARs. */ + if (*s == ')' || *s == ',') + { + token.u.ch = *s; + nanomips_add_token (&token, OT_CHAR); + ++s; + return s; + } + + /* Handle tokens that start with a register. */ + if (nanomips_parse_register (&s, ®no1)) + { + SKIP_SPACE_TABS (s); + if (*s == '-') + { + /* A register range. */ + ++s; + SKIP_SPACE_TABS (s); + if (!nanomips_parse_register (&s, ®no2)) + { + set_insn_error (0, _("invalid register range")); + return 0; + } + + token.u.reg_range.regno1 = regno1; + token.u.reg_range.regno2 = regno2; + nanomips_add_token (&token, OT_REG_RANGE); + return s; + } + + /* Add the register itself. */ + token.u.regno = regno1; + nanomips_add_token (&token, OT_REG); + + /* Check for a vector index. */ + if (*s == '[') + { + ++s; + SKIP_SPACE_TABS (s); + if (nanomips_parse_register (&s, &token.u.regno)) + nanomips_add_token (&token, OT_REG_INDEX); + else + { + expressionS element; + + my_getExpression (&element, s); + if (element.X_op != O_constant) + { + set_insn_error (0, _("vector element must be constant")); + return 0; + } + s = expr_parse_end; + token.u.index = element.X_add_number; + nanomips_add_token (&token, OT_INTEGER_INDEX); + } + SKIP_SPACE_TABS (s); + if (*s != ']') + { + set_insn_error (0, _("missing `]'")); + return 0; + } + ++s; + } + return s; + } + + if (float_format) + { + /* First try to treat expressions as floats. */ + save_in = input_line_pointer; + input_line_pointer = s; + err = md_atof (float_format, (char *) token.u.flt.data, + &token.u.flt.length); + end = input_line_pointer; + input_line_pointer = save_in; + if (err && *err) + { + set_insn_error (0, err); + return 0; + } + if (s != end) + { + nanomips_add_token (&token, OT_FLOAT); + return end; + } + } + + /* Treat everything else as an integer expression. */ + token.u.integer.relocs[0] = BFD_RELOC_UNUSED; + token.u.integer.relocs[1] = BFD_RELOC_UNUSED; + token.u.integer.relocs[2] = BFD_RELOC_UNUSED; + my_getSmallExpression (&token.u.integer.value, token.u.integer.relocs, s); + s = expr_parse_end; + nanomips_add_token (&token, OT_INTEGER); + return s; +} + +/* S points to the operand list for an instruction. FLOAT_FORMAT is 'f' + if expressions should be treated as 32-bit floating-point constants, + 'd' if they should be treated as 64-bit floating-point constants, + or 0 if they should be treated as integer expressions (the usual case). + + Return a list of tokens on success, otherwise return 0. The caller + must obstack_free the list after use. */ + +static struct nanomips_operand_token * +nanomips_parse_arguments (char *s, char float_format) +{ + struct nanomips_operand_token token; + + SKIP_SPACE_TABS (s); + while (*s) + { + s = nanomips_parse_argument_token (s, float_format); + if (!s) + { + obstack_free (&nanomips_operand_tokens, + obstack_base (&nanomips_operand_tokens)); + return 0; + } + SKIP_SPACE_TABS (s); + } + nanomips_add_token (&token, OT_END); + return obstack_finish (&nanomips_operand_tokens); +} + +/* Return true if opcode MO is valid for the currently selected + floating point configuration. */ + +static bool +is_opcode_valid_for_fp (const struct nanomips_opcode *mo) +{ + int fp_s, fp_d; + + /* Check whether the instruction or macro requires single-precision or + double-precision floating-point support. Note that this information is + stored differently in the opcode table for insns and macros. */ + if (mo->pinfo == INSN_MACRO) + { + fp_s = mo->pinfo2 & INSN2_M_FP_S; + fp_d = mo->pinfo2 & INSN2_M_FP_D; + } + else + { + fp_s = mo->pinfo & INSN_FP_S; + fp_d = mo->pinfo & INSN_FP_D; + } + + if (fp_d && (nanomips_opts.soft_float || nanomips_opts.single_float)) + return false; + + if (fp_s && nanomips_opts.soft_float) + return false; + + return true; +} + +/* Return true if opcode MO is valid for the currently selected ISA + with the specified ASE. */ + +static bool +is_opcode_valid_for_ase (const struct nanomips_opcode *mo, int ase) +{ + int isa = nanomips_opts.isa; + unsigned int i; + + if (ISA_HAS_64BIT_REGS (isa)) + for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++) + if ((ase & nanomips_ases[i].flags) == nanomips_ases[i].flags) + ase |= nanomips_ases[i].flags64; + + return nanomips_opcode_is_member (mo, isa, ase, nanomips_opts.arch); +} + +/* Return true if opcode MO is valid for the currently selected ISA, + ASE and floating point configuration. */ + +static bool +is_opcode_valid (const struct nanomips_opcode *mo) +{ + return (is_opcode_valid_for_ase (mo, nanomips_opts.ase) + && is_opcode_valid_for_fp (mo)); +} + +/* Return true if opcode MO is valid for the currently selected ISA + and architecture with a default ASE selection. */ + +static bool +is_opcode_valid_def_ase (const struct nanomips_opcode *mo) +{ + const struct nanomips_cpu_info *info; + int ase; + + info = nanomips_cpu_info_from_isa (nanomips_opts.isa, + (nanomips_opts.ase & ASE_xNMS) == 0); + ase = info->ase | nanomips_opts.ase; + + return is_opcode_valid_for_ase (mo, ase); +} + +/* Return true if the size of the nanoMIPS opcode MO matches one + explicitly requested. */ + +static bool +is_size_valid (const struct nanomips_opcode *mo) +{ + if (nanomips_opts.insn32 && mo->pinfo != INSN_MACRO && insn_length (mo) != 4) + return false; + + if (!forced_insn_length) + return true; + + if (mo->pinfo == INSN_MACRO) + return false; + + return forced_insn_length == insn_length (mo); +} + +/* For consistency checking, verify that all bits of OPCODE are specified + either by the match/mask part of the instruction definition, or by the + operand list. Also build up a list of operands in OPERANDS. + + INSN_BITS says which bits of the instruction are significant. + DECODE_OPERAND rovides the nanomips_operand description of each operand. */ + +static int +validate_insn (const struct nanomips_opcode *opcode, unsigned long insn_bits, + struct nanomips_operand_array *operands) +{ + const char *s; + unsigned long used_bits, doubled, undefined, opno, mask; + const struct nanomips_operand *operand; + + mask = (opcode->pinfo == INSN_MACRO ? 0 : opcode->mask); + if ((mask & opcode->match) != opcode->match) + { + as_bad (_("internal: bad nanomips opcode (mask error): %s %s"), + opcode->name, opcode->args); + return 0; + } + used_bits = 0; + opno = 0; + + for (s = opcode->args; *s; ++s) + switch (*s) + { + case ',': + case '(': + case ')': + break; + + case '#': + s++; + break; + + default: + operand = decode_nanomips_operand (s); + if (!operand && opcode->pinfo != INSN_MACRO) + { + as_bad (_("internal: unknown operand type: %s %s"), + opcode->name, opcode->args); + return 0; + } + gas_assert (opno < MAX_OPERANDS); + operands->operand[opno] = operand; + if (operand != NULL) + used_bits = nanomips_insert_operand (operand, used_bits, -1); + /* Skip prefix characters. */ + if (*s == '+' || *s == 'm' || *s == '-') + ++s; + opno += 1; + break; + } + doubled = used_bits & mask & insn_bits; + if (doubled) + { + as_bad (_("internal: bad nanomips opcode (bits 0x%08lx doubly defined):" + " %s %s"), doubled, opcode->name, opcode->args); + return 0; + } + used_bits |= mask; + undefined = ~used_bits & insn_bits; + if (opcode->pinfo != INSN_MACRO && undefined) + { + as_bad (_("internal: bad nanomips opcode (bits 0x%08lx undefined): " + "%s %s"), undefined, opcode->name, opcode->args); + return 0; + } + used_bits &= ~insn_bits; + if (used_bits) + { + as_bad (_("internal: bad nanomips opcode (bits 0x%08lx defined): " + "%s %s"), used_bits, opcode->name, opcode->args); + return 0; + } + return 1; +} + +static int +validate_nanomips_insn (const struct nanomips_opcode *opc, + struct nanomips_operand_array *operands) +{ + unsigned long insn_bits; + unsigned long major; + unsigned int length; + + if (opc->pinfo == INSN_MACRO) + return validate_insn (opc, 0xffffffff, operands); + + length = insn_length (opc); + if (length != 2 && length != 4 && length != 6) + { + as_bad (_("internal error: bad nanoMIPS opcode (incorrect length: %u): " + "%s %s"), length, opc->name, opc->args); + return 0; + } + + if (length == 6) + /* 48-bit instruction is encoded as 16-bits in the opcode table. */ + major = opc->match >> 10; + else + major = opc->match >> (10 + 8 * (length - 2)); + + if ((length == 2 && !(major & 4)) + || (length == 4 && (major & 4)) + || (length == 6 && !(major & 18))) + { + as_bad (_("internal error: bad nanoMIPS opcode " + "(opcode/length mismatch): %s %s"), opc->name, opc->args); + return 0; + } + + if (length == 6) + /* 48-bit instruction is encoded as 16-bits in the opcode table. */ + insn_bits = 1 << 16; + else + { + /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */ + insn_bits = 1 << 4 * length; + insn_bits <<= 4 * length; + } + + insn_bits -= 1; + return validate_insn (opc, insn_bits, operands); +} + +/* This function is called once, at assembler startup time. It should set up + all the tables, etc. that the MD part of the assembler will need. */ + +void +md_begin (void) +{ + int i = 0; + int broken = 0; + int count_marker = 0; + + if (nanomips_opts.pic != NO_PIC) + { + if (g_switch_seen && g_switch_value != 0) + as_bad (_("-G may not be used in position-independent code")); + g_switch_value = 0; + } + + if (!bfd_set_arch_mach (stdoutput, bfd_arch_nanomips, + file_nanomips_opts.arch)) + as_warn (_("could not set architecture and machine")); + + op_hash = str_htab_create (); + + nanomips_op_hash = str_htab_create (); + nanomips_operands = XCNEWVEC (struct nanomips_operand_array, + bfd_nanomips_num_opcodes); + + i = 0; + while (i < bfd_nanomips_num_opcodes) + { + const char *name = nanomips_opcodes[i].name; + + if (str_hash_insert (nanomips_op_hash, name, &nanomips_opcodes[i], 0)) + as_fatal (_("duplicate %s"), name); + do + { + struct nanomips_cl_insn *insn; + + if (!validate_nanomips_insn (&nanomips_opcodes[i], + &nanomips_operands[i])) + broken = 1; + + if (count_marker < 3 && nanomips_opcodes[i].pinfo != INSN_MACRO) + { + if (nanomips_nop16_insn.insn_mo == NULL + && insn_length (&nanomips_opcodes[i]) == 2 + && strcmp (name, "nop") == 0) + insn = &nanomips_nop16_insn; + else if (nanomips_nop32_insn.insn_mo == NULL + && insn_length (&nanomips_opcodes[i]) == 4 + && strcmp (name, "nop") == 0) + insn = &nanomips_nop32_insn; + else if (nanomips_bc32_insn.insn_mo == NULL + && insn_length (&nanomips_opcodes[i]) == 4 + && strcmp (name, "bc") == 0) + insn = &nanomips_bc32_insn; + else + continue; + + create_insn (insn, nanomips_opcodes + i); + insn->fixed_p = 1; + count_marker++; + } + } + while (++i < bfd_nanomips_num_opcodes + && strcmp (nanomips_opcodes[i].name, name) == 0); + } + + i = 0; + while (i < bfd_nanomips_num_opcodes) + { + const struct nanomips_opcode *opcode = &nanomips_opcodes[i]; + if (opcode->suffix[0] != '\0') + { + char *name; + name = xmalloc (strlen (opcode->name) + strlen (opcode->suffix) + 1); + name = strcpy (name, opcode->name); + name = strcat (name, opcode->suffix); + + if (str_hash_find (nanomips_op_hash, name) == NULL) + { + if (str_hash_insert (nanomips_op_hash, name, &nanomips_opcodes[i], 0)) + as_fatal (_("duplicate %s"), name); + } + } + i++; + } + + if (broken) + as_fatal (_("broken assembler, no assembly attempted")); + + /* We add all the general register names to the symbol table. This + helps us detect invalid uses of them. */ + for (i = 0; nanomips_reg_names[i].name; i++) + { + symbolS *regsym = symbol_new (nanomips_reg_names[i].name, reg_section, + &zero_address_frag, + nanomips_reg_names[i].num); + symbolS *defsym = symbol_find (nanomips_reg_names[i].name); + symbol_table_insert (regsym); + if (defsym) + as_warn ("Attempt to define internal register symbol %s ignored", + nanomips_reg_names[i].name); + } + + for (i = 0; nanomips_cp0_3264r6[i].name; i++) + { + /* 10-bit symbol value for CP0 named register consists of a 5-bit + register number and 5-bit fixed select value. */ + unsigned int value = (RTYPE_CP0 + | (nanomips_cp0_3264r6[i].num + << NANOMIPSOP_SH_CP0SEL) + | nanomips_cp0_3264r6[i].sel); + symbolS *regsym = symbol_new (nanomips_cp0_3264r6[i].name, reg_section, + &zero_address_frag, value); + symbolS *defsym = symbol_find (nanomips_cp0_3264r6[i].name); + symbol_table_insert (regsym); + if (defsym) + as_warn ("Attempt to define internal register symbol %s ignored", + nanomips_cp0_3264r6[i].name); + } + + for (i = 0; nanomips_cp0sel_3264r6[i].name; i++) + { + symbolS *regsym, *defsym; + unsigned value = nanomips_cp0sel_3264r6[i].num; + + switch (nanomips_cp0sel_3264r6[i].selmask) + { + case NANOMIPS_CP0SEL_MASK_EVEN: + value |= RTYPE_CP0SEL_EVEN; + break; + case NANOMIPS_CP0SEL_MASK_ODD: + value |= RTYPE_CP0SEL_ODD; + break; + case NANOMIPS_CP0SEL_MASK_ANY: + value |= RTYPE_CP0SEL; + break; + default: + break; + } + + regsym = symbol_new (nanomips_cp0sel_3264r6[i].name, reg_section, + &zero_address_frag, value); + defsym = symbol_find (nanomips_cp0sel_3264r6[i].name); + symbol_table_insert (regsym); + if (defsym) + as_warn ("Attempt to define internal register symbol %s ignored", + nanomips_cp0sel_3264r6[i].name); + } + + obstack_init (&nanomips_operand_tokens); + + nanomips_flush_pending_output (); + + bfd_set_gp_size (stdoutput, g_switch_value); + + { + segT seg; + subsegT subseg; + segT sec; + + seg = now_seg; + subseg = now_subseg; + + subseg_set (seg, subseg); + } +} + +static inline void +fpabi_incompatible_with (int fpabi, const char *what) +{ + as_warn (_(".gnu_attribute %d,%d is incompatible with `%s'"), + Tag_GNU_NANOMIPS_ABI_FP, fpabi, what); +} + +static inline void +fpabi_requires (int fpabi, const char *what) +{ + as_warn (_(".gnu_attribute %d,%d requires `%s'"), + Tag_GNU_NANOMIPS_ABI_FP, fpabi, what); +} + +/* Check -mabi and register sizes against the specified FP ABI. */ +static void +check_fpabi (int fpabi) +{ + switch (fpabi) + { + case Val_GNU_NANOMIPS_ABI_FP_DOUBLE: + if (file_nanomips_opts.soft_float) + fpabi_incompatible_with (fpabi, "softfloat"); + else if (file_nanomips_opts.single_float) + fpabi_incompatible_with (fpabi, "singlefloat"); + break; + + case Val_GNU_NANOMIPS_ABI_FP_SINGLE: + if (file_nanomips_opts.soft_float) + fpabi_incompatible_with (fpabi, "softfloat"); + else if (!file_nanomips_opts.single_float) + fpabi_requires (fpabi, "singlefloat"); + break; + + case Val_GNU_NANOMIPS_ABI_FP_SOFT: + if (!file_nanomips_opts.soft_float) + fpabi_requires (fpabi, "softfloat"); + break; + + default: + as_warn (_(".gnu_attribute %d,%d is not a recognized" + " floating-point ABI"), Tag_GNU_NANOMIPS_ABI_FP, fpabi); + break; + } +} + +/* Perform consistency checks on the module level options exactly once. + This is a deferred check that happens: + at the first .set directive + or, at the first pseudo op that generates code (inc .dc.a) + or, at the first instruction + or, at the end. */ + +static void +file_check_options (void) +{ + const struct nanomips_cpu_info *arch_info = 0; + if (file_nanomips_opts_checked) + return; + + /* No user specified integer register size. */ + /* Infer the integer register size from the ABI. */ + if (file_nanomips_opts.gp < 0) + file_nanomips_opts.gp = ((ABI_NEEDS_32BIT_REGS (nanomips_abi)) ? 32 : 64); + + if (file_nanomips_opts.fp < 0) + /* No user specified float register size. */ + /* nanoMIPS implies 64-bit float registers. */ + file_nanomips_opts.fp = 64; + + /* This flag is set when we have a 64-bit capable CPU but use only + 32-bit wide registers. Note that EABI does not use it. */ + if (ISA_HAS_64BIT_REGS (file_nanomips_opts.isa) + && ((nanomips_abi == NO_ABI && file_nanomips_opts.gp == 32) + || nanomips_abi == P32_ABI)) + nanomips_32bitmode = 1; + + /* If the user didn't explicitly select or deselect a particular ASE, + use the default setting for the CPU. */ + file_nanomips_opts.ase |= + (file_nanomips_opts.init_ase & ~file_ase_explicit); + + /* Set up the current options. These may change throughout assembly. */ + nanomips_opts = file_nanomips_opts; + + check_isa_supports_ases (); + file_nanomips_opts_checked = true; + + if (!bfd_set_arch_mach (stdoutput, bfd_arch_nanomips, + file_nanomips_opts.arch)) + as_warn (_("could not set architecture and machine")); +} + +void +md_assemble (char *str) +{ + struct nanomips_cl_insn insn; + bfd_reloc_code_real_type unused_reloc[3] + = { BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED }; + + file_check_options (); + + imm_expr.X_op = O_absent; + offset_expr.X_op = O_absent; + offset_reloc[0] = BFD_RELOC_UNUSED; + offset_reloc[1] = BFD_RELOC_UNUSED; + offset_reloc[2] = BFD_RELOC_UNUSED; + + if (cur_proc_ptr == NULL && !stub_funcless_mode) + { + stubgroup_new (now_seg); + stub_funcless_mode = true; + } + + clear_insn_error (); + nanomips_ip (str, &insn); + + if (insn_error.msg) + report_insn_error (str); + else if (insn.insn_mo->pinfo == INSN_MACRO) + { + macro_start (); + macro (&insn, str); + macro_end ((insn.insn_mo->pinfo2 & INSN2_MACRO) != 0); + } + else + { + if (offset_expr.X_op != O_absent) + append_insn (&insn, &offset_expr, offset_reloc, false); + else + append_insn (&insn, NULL, unused_reloc, false); + } +} + +static inline bool +nanomips_reloc_p (bfd_reloc_code_real_type reloc) +{ + switch (reloc) + { + case BFD_RELOC_NANOMIPS_GPREL19_S2: + case BFD_RELOC_NANOMIPS_GPREL18_S3: + case BFD_RELOC_NANOMIPS_GPREL16_S2: + case BFD_RELOC_NANOMIPS_HI20: + case BFD_RELOC_NANOMIPS_LO12: + case BFD_RELOC_NANOMIPS_PCREL_HI20: + case BFD_RELOC_NANOMIPS_7_PCREL_S1: + case BFD_RELOC_NANOMIPS_10_PCREL_S1: + case BFD_RELOC_NANOMIPS_11_PCREL_S1: + case BFD_RELOC_NANOMIPS_21_PCREL_S1: + case BFD_RELOC_NANOMIPS_25_PCREL_S1: + case BFD_RELOC_NANOMIPS_14_PCREL_S1: + case BFD_RELOC_NANOMIPS_GOT_CALL: + case BFD_RELOC_NANOMIPS_GOTPC_HI20: + case BFD_RELOC_NANOMIPS_GOT_LO12: + case BFD_RELOC_NANOMIPS_GOT_PAGE: + case BFD_RELOC_NANOMIPS_GOT_OFST: + case BFD_RELOC_NANOMIPS_GOT_DISP: + case BFD_RELOC_NANOMIPS_IMM16: + case BFD_RELOC_NANOMIPS_NEG12: + case BFD_RELOC_NANOMIPS_I32: + case BFD_RELOC_NANOMIPS_TLS_GD: + case BFD_RELOC_NANOMIPS_TLS_GD_I32: + case BFD_RELOC_NANOMIPS_TLS_LD: + case BFD_RELOC_NANOMIPS_TLS_LD_I32: + case BFD_RELOC_NANOMIPS_TLS_DTPREL12: + case BFD_RELOC_NANOMIPS_TLS_DTPREL16: + case BFD_RELOC_NANOMIPS_TLS_DTPREL_I32: + case BFD_RELOC_NANOMIPS_TLS_TPREL12: + case BFD_RELOC_NANOMIPS_TLS_TPREL16: + case BFD_RELOC_NANOMIPS_TLS_TPREL_I32: + case BFD_RELOC_NANOMIPS_TLS_GOTTPREL: + case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32: + case BFD_RELOC_NANOMIPS_PC_I32: + case BFD_RELOC_NANOMIPS_GOTPC_I32: + case BFD_RELOC_NANOMIPS_GPREL_I32: + case BFD_RELOC_NANOMIPS_GPREL18: + case BFD_RELOC_NANOMIPS_GPREL17_S1: + case BFD_RELOC_NANOMIPS_GPREL_HI20: + case BFD_RELOC_NANOMIPS_GPREL_LO12: + case BFD_RELOC_NANOMIPS_SIGNED_9: + return true; + + default: + return false; + } +} + +static inline bool +nanomips_48bit_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_NANOMIPS_I32 + || reloc == BFD_RELOC_NANOMIPS_PC_I32 + || reloc == BFD_RELOC_NANOMIPS_GPREL_I32 + || reloc == BFD_RELOC_NANOMIPS_GOTPC_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_GD_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_LD_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_DTPREL_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_TPREL_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32); +} + +static inline bool +hi_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_HI16_S + || reloc == BFD_RELOC_NANOMIPS_HI20 + || reloc == BFD_RELOC_NANOMIPS_GOTPC_HI20 + || reloc == BFD_RELOC_NANOMIPS_GPREL_HI20); +} + +static inline bool +lo_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_LO16 + || reloc == BFD_RELOC_NANOMIPS_LO12 + || reloc == BFD_RELOC_NANOMIPS_GPREL_LO12 + || reloc == BFD_RELOC_NANOMIPS_GOT_LO12); +} + +static inline bool +gprel_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_GPREL16 + || reloc == BFD_RELOC_NANOMIPS_GPREL7_S2 + || reloc == BFD_RELOC_NANOMIPS_GPREL18 + || reloc == BFD_RELOC_NANOMIPS_GPREL19_S2 + || reloc == BFD_RELOC_NANOMIPS_GPREL16_S2 + || reloc == BFD_RELOC_NANOMIPS_GPREL18_S3 + || reloc == BFD_RELOC_NANOMIPS_GPREL17_S1 + || reloc == BFD_RELOC_NANOMIPS_TLS_GD + || reloc == BFD_RELOC_NANOMIPS_TLS_LD); +} + +static inline bool +pcrel16_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_NANOMIPS_4_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_7_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_10_PCREL_S1); +} + +static inline bool +pcrel32_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_32_PCREL + || reloc == BFD_RELOC_NANOMIPS_11_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_14_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_21_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_25_PCREL_S1 + || reloc == BFD_RELOC_NANOMIPS_PCREL_HI20 + || reloc == BFD_RELOC_NANOMIPS_GOTPC_HI20); +} + +static inline bool +pcrel_branch_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (pcrel16_reloc_p (reloc) + || (pcrel32_reloc_p (reloc) + && reloc != BFD_RELOC_NANOMIPS_PCREL_HI20 + && reloc != BFD_RELOC_NANOMIPS_GOTPC_HI20)); +} + +static inline bool +pcrel48_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (reloc == BFD_RELOC_NANOMIPS_PC_I32 + || reloc == BFD_RELOC_NANOMIPS_GOTPC_I32 + || reloc == BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32); +} + +static inline bool +pcrel_reloc_p (bfd_reloc_code_real_type reloc) +{ + return (pcrel16_reloc_p (reloc) + || pcrel32_reloc_p (reloc) + || pcrel48_reloc_p (reloc)); +} + +/* Return true if RELOC is a PC-relative relocation that does not have + full address range. */ + +static inline bool +limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc) +{ + switch (reloc) + { + case BFD_RELOC_NANOMIPS_4_PCREL_S1: + case BFD_RELOC_NANOMIPS_7_PCREL_S1: + case BFD_RELOC_NANOMIPS_10_PCREL_S1: + case BFD_RELOC_NANOMIPS_11_PCREL_S1: + case BFD_RELOC_NANOMIPS_14_PCREL_S1: + case BFD_RELOC_NANOMIPS_21_PCREL_S1: + case BFD_RELOC_NANOMIPS_25_PCREL_S1: + return true; + + case BFD_RELOC_32_PCREL: + case BFD_RELOC_NANOMIPS_PCREL_HI20: + return HAVE_64BIT_ADDRESSES; + + default: + return false; + } +} + +/* Return true if RELOC is a place-holder for linker relaxation and + not associated with a particular instruction. */ + +static inline bool +linkrelax_reloc_p (bfd_reloc_code_real_type reloc) +{ + switch (reloc) + { + case BFD_RELOC_NANOMIPS_ALIGN: + case BFD_RELOC_NANOMIPS_FILL: + case BFD_RELOC_NANOMIPS_MAX: + case BFD_RELOC_NANOMIPS_FIXED: + case BFD_RELOC_NANOMIPS_RELAX: + case BFD_RELOC_NANOMIPS_NORELAX: + return true; + default: + return false; + } +} + +/* Return true if R is an initial selected relocation that has multiple + size variants. */ + +static bool +flex_reloc_p (bfd_reloc_code_real_type r) +{ + return (r == BFD_RELOC_NANOMIPS_GPREL18 + || r == BFD_RELOC_NANOMIPS_TLS_LD + || r == BFD_RELOC_NANOMIPS_TLS_GD + || r == BFD_RELOC_NANOMIPS_TLS_TPREL12 + || r == BFD_RELOC_NANOMIPS_TLS_DTPREL12); +} + + +/* Return true if R is GP-relative relocation that takes 32-bit + immediate displacement. */ + +static bool +gprel_i32_reloc_p (bfd_reloc_code_real_type r) +{ + return (r == BFD_RELOC_NANOMIPS_GPREL_I32 + || r == BFD_RELOC_NANOMIPS_TLS_LD_I32 + || r == BFD_RELOC_NANOMIPS_TLS_GD_I32); +} + +/* Return the type of %lo() reloc needed by RELOC, given that + reloc_needs_lo_p. */ + +static inline bfd_reloc_code_real_type +matching_lo_reloc (bfd_reloc_code_real_type reloc ATTRIBUTE_UNUSED) +{ + return (BFD_RELOC_LO16); +} + +/* Return true if the given fixup is followed by a matching %lo + relocation. */ + +static inline bool +fixup_has_matching_lo_p (fixS *fixp) +{ + return (fixp->fx_next != NULL + && fixp->fx_next->fx_r_type == matching_lo_reloc (fixp->fx_r_type) + && fixp->fx_addsy == fixp->fx_next->fx_addsy + && fixp->fx_offset == fixp->fx_next->fx_offset); +} + +/* Move all labels in LABELS to the current insertion point. */ + +static void +nanomips_move_labels (struct insn_label_list *labels) +{ + struct insn_label_list *l; + valueT val; + segment_info_type *si = seg_info (now_seg); + + if (toggle_linkrelax_p) + toggle_linkrelax (frag_now, frag_now_fix ()); + + for (l = si->label_list; l != NULL; l = l->next) + { + gas_assert (S_GET_SEGMENT (l->label) == now_seg); + symbol_set_frag (l->label, frag_now); + val = (valueT) frag_now_fix (); + S_SET_VALUE (l->label, val); + } +} + +static bool +s_is_linkonce (symbolS *sym, segT from_seg) +{ + bool linkonce = false; + segT symseg = S_GET_SEGMENT (sym); + + if (symseg != from_seg && !S_IS_LOCAL (sym)) + { + if ((bfd_section_flags (symseg) & SEC_LINK_ONCE)) + linkonce = true; + /* The GNU toolchain uses an extension for ELF: a section + beginning with the magic string .gnu.linkonce is a + linkonce section. */ + if (strncmp (segment_name (symseg), ".gnu.linkonce", + sizeof ".gnu.linkonce" - 1) == 0) + linkonce = true; + } + return linkonce; +} + +/* End the current frag. Make it a variant frag and record the + relaxation info. */ + +static void +relax_close_frag (void) +{ + nanomips_macro_warning.first_frag = frag_now; + frag_var (rs_machine_dependent, 0, 0, + RELAX_ENCODE (nanomips_relax.sizes[0], nanomips_relax.sizes[1]), + nanomips_relax.symbol, 0, (char *) nanomips_relax.first_fixup); + + memset (&nanomips_relax.sizes, 0, sizeof (nanomips_relax.sizes)); + nanomips_relax.first_fixup = 0; +} + +/* Start a new relaxation sequence whose expansion depends on SYMBOL. + See the comment above RELAX_ENCODE for more details. */ + +static void +relax_start (symbolS *symbol) +{ + gas_assert (nanomips_relax.sequence == 0); + nanomips_relax.sequence = 1; + nanomips_relax.symbol = symbol; +} + +/* Start generating the second version of a relaxable sequence. + See the comment above RELAX_ENCODE for more details. */ + +static void +relax_switch (void) +{ + gas_assert (nanomips_relax.sequence == 1); + nanomips_relax.sequence = 2; +} + +/* End the current relaxable sequence. */ + +static void +relax_end (void) +{ + gas_assert (nanomips_relax.sequence == 2); + relax_close_frag (); + nanomips_relax.sequence = 0; +} + +/* Return true if IP is a compact branch or jump. */ + +static inline bool +compact_branch_p (const struct nanomips_cl_insn *ip) +{ + return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH + | INSN2_COND_BRANCH)) != 0; +} + +/* Return true if IP is an unconditional branch or jump. */ + +static inline bool +uncond_branch_p (const struct nanomips_cl_insn *ip) +{ + return ((ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0); +} + +/* Information about an instruction argument that we're trying to match. */ +struct nanomips_arg_info +{ + /* The instruction so far. */ + struct nanomips_cl_insn *insn; + + /* The first unconsumed operand token. */ + struct nanomips_operand_token *token; + + /* The 1-based operand number, in terms of insn->insn_mo->args. */ + int opnum; + + /* The 1-based argument number, for error reporting. This does not + count elided optional registers, etc.. */ + int argnum; + + /* The last OP_REG operand seen, or ILLEGAL_REG if none. */ + unsigned int last_regno; + + /* If the first operand was an OP_REG, this is the register that it + specified, otherwise it is ILLEGAL_REG. */ + unsigned int dest_regno; + + /* The value of the last OP_INT operand. Used for OP_MSB, + where it gives the lsb position and to check stack frame size + for save/restore register list. */ + unsigned int last_op_int; + + /* If true, match routines should assume that no later instruction + alternative matches and should therefore be as accomodating as + possible. Match routines should not report errors if something + is only invalid for !LAX_MATCH. */ + bool lax_match; + + /* True if a reference to the current AT register was seen. */ + bool seen_at; + + /* CP0 select register mask. */ + unsigned int select_mask; +}; + +/* Record that the argument is out of range. */ + +static void +match_out_of_range (struct nanomips_arg_info *arg) +{ + set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum); +} + +/* Record that the argument isn't constant but needs to be. */ + +static void +match_not_constant (struct nanomips_arg_info *arg) +{ + set_insn_error_i (arg->argnum, _("operand %d must be constant"), + arg->argnum); +} + +/* Try to match an OT_CHAR token for character CH. Consume the token + and return true on success, otherwise return false. */ + +static bool +match_char (struct nanomips_arg_info *arg, char ch) +{ + if (arg->token->type == OT_CHAR && arg->token->u.ch == ch) + { + ++arg->token; + if (ch == ',') + arg->argnum += 1; + return true; + } + return false; +} + +/* Try to get an expression from the next tokens in ARG. Consume the + tokens and return true on success, storing the expression value in + VALUE and relocation types in R. */ + +static bool +match_expression (struct nanomips_arg_info *arg, expressionS *value, + bfd_reloc_code_real_type *r) +{ + /* If the next token is a '(' that was parsed as being part of a base + expression, assume we have an elided offset. The later match will fail + if this turns out to be wrong. */ + if (arg->token->type == OT_CHAR && arg->token->u.ch == '(') + { + value->X_op = O_constant; + value->X_add_number = 0; + r[0] = r[1] = r[2] = BFD_RELOC_UNUSED; + return true; + } + + /* Reject register-based expressions such as "0+$2" and "(($2))". + For plain registers the default error seems more appropriate. */ + if (arg->token->type == OT_INTEGER + && arg->token->u.integer.value.X_op == O_register) + { + set_insn_error (arg->argnum, _("register value used as expression")); + return false; + } + + if (arg->token->type == OT_INTEGER) + { + *value = arg->token->u.integer.value; + memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r)); + ++arg->token; + return true; + } + + set_insn_error_i + (arg->argnum, _("operand %d must be an immediate expression"), + arg->argnum); + return false; +} + +/* Try to get a constant expression from the next tokens in ARG. Consume + the tokens and return return true on success, storing the constant value + in *VALUE. Use FALLBACK as the value if the match succeeded with an + error. */ + +static bool +match_const_int (struct nanomips_arg_info *arg, offsetT *value) +{ + expressionS ex; + bfd_reloc_code_real_type r[3]; + + if (!match_expression (arg, &ex, r)) + return false; + + if (r[0] == BFD_RELOC_UNUSED && ex.X_op == O_constant) + *value = ex.X_add_number; + else + { + match_not_constant (arg); + return false; + } + return true; +} + +/* Return the RTYPE_* flags for a register operand of type TYPE that + appears in instruction OPCODE. */ + +static unsigned int +convert_reg_type (const struct nanomips_opcode *opcode, + enum nanomips_reg_operand_type type) +{ + switch (type) + { + case OP_REG_GP: + default: + if (!nanomips_opts.legacyregs) + return RTYPE_GP; + else + return RTYPE_NUM | RTYPE_GP; + + case OP_REG_FP: + return RTYPE_FPU; + + case OP_REG_ACC: + return RTYPE_ACC; + + case OP_REG_COPRO: + if (opcode->name[strlen (opcode->name) - 1] == '0') + return RTYPE_NUM | RTYPE_CP0; + return RTYPE_NUM; + + case OP_REG_HW: + return RTYPE_NUM; + + case OP_REG_CP0: + /* CP0 register without select must be symbolic. */ + return RTYPE_CP0; + + case OP_REG_CP0SEL: + /* CP0 register with select may be numeric. */ + return RTYPE_CP0SEL | RTYPE_NUM; + } + abort (); +} + +/* ARG is register REGNO, of type TYPE. Warn about any dubious registers. */ + +static void +check_regno (struct nanomips_arg_info *arg, + enum nanomips_reg_operand_type type, unsigned int regno) +{ + if (AT && type == OP_REG_GP && regno == AT) + arg->seen_at = true; +} + +/* ARG is a register with symbol value SYMVAL. Try to interpret it as + a register of type TYPE. Return true on success, storing the register + number in *REGNO and warning about any dubious uses. */ + +static bool +match_regno (struct nanomips_arg_info *arg, + enum nanomips_reg_operand_type type, unsigned int symval, + unsigned int *regno) +{ + if (!(symval & convert_reg_type (arg->insn->insn_mo, type))) + return false; + + /* Remember if the register name matches a specific select mask type. + Otherwise fall-back to allowing all select values. */ + if (type == OP_REG_CP0SEL) + switch (symval & RTYPE_CP0SEL) + { + case RTYPE_CP0SEL_EVEN: + arg->select_mask = NANOMIPS_CP0SEL_MASK_EVEN; + break; + case RTYPE_CP0SEL_ODD: + arg->select_mask = NANOMIPS_CP0SEL_MASK_ODD; + break; + case RTYPE_CP0SEL: + default: + arg->select_mask = NANOMIPS_CP0SEL_MASK_ANY; + break; + } + + *regno = symval & RNUM_MASK; + check_regno (arg, type, *regno); + return true; +} + +/* Try to interpret the next token in ARG as a register of type TYPE. + Consume the token and return true on success, storing the register + number in *REGNO. Return false on failure. */ + +static bool +match_reg (struct nanomips_arg_info *arg, enum nanomips_reg_operand_type type, + unsigned int *regno) +{ + if (arg->token->type == OT_REG + && match_regno (arg, type, arg->token->u.regno, regno)) + { + ++arg->token; + return true; + } + return false; +} + +/* Try to interpret the next token in ARG as a range of registers of type TYPE. + Consume the token and return true on success, storing the register numbers + in *REGNO1 and *REGNO2. Return false on failure. */ + +static bool +match_reg_range (struct nanomips_arg_info *arg, + enum nanomips_reg_operand_type type, unsigned int *regno1, + unsigned int *regno2) +{ + if (match_reg (arg, type, regno1)) + { + *regno2 = *regno1; + return true; + } + if (arg->token->type == OT_REG_RANGE + && match_regno (arg, type, arg->token->u.reg_range.regno1, regno1) + && match_regno (arg, type, arg->token->u.reg_range.regno2, regno2) + && *regno1 <= *regno2) + { + ++arg->token; + return true; + } + return false; +} + +/* OP_INT matcher. */ + +static bool +match_relocatable_int_operand (const struct nanomips_operand *operand_base) +{ + const struct nanomips_int_operand *operand; + unsigned int op_size; + + operand = (const struct nanomips_int_operand *) operand_base; + op_size = operand_base->size; + + if ((op_size == 7 && operand->shift == 2) + || (op_size == 4 && operand->shift == 2) + || op_size == 12 + || op_size == 14 + || op_size == 16 + || op_size == 17 + || op_size == 18 + || op_size == 19 + || op_size == 20) + return true; + + return false; +} + +/* Negative 12-bit constant matcher. */ + +static bool +match_negative_int_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + offsetT sval; + + if (!match_const_int (arg, &sval)) + return false; + if ((int) sval > 0 || (int) sval < -4095) + return false; + sval = -(int) sval; + + insn_insert_operand (arg->insn, operand_base, sval); + return true; +} + +/* Check if OPERAND_BASE matches a suitable variant of relocation RTYPE. */ + +static bool +match_flex_reloc_for_int_operand (const struct nanomips_operand *operand_base, + bfd_reloc_code_real_type rtype) +{ + size_t i; + int opmask = nanomips_operand_mask (operand_base); + const bfd_reloc_code_real_type gp_relocs[] = { + BFD_RELOC_NANOMIPS_GPREL18, + BFD_RELOC_NANOMIPS_GPREL19_S2, + BFD_RELOC_NANOMIPS_GPREL18_S3, + BFD_RELOC_NANOMIPS_GPREL16_S2, + BFD_RELOC_NANOMIPS_GPREL17_S1, + BFD_RELOC_NANOMIPS_GPREL7_S2 + }; + const bfd_reloc_code_real_type tp_relocs[] = { + BFD_RELOC_NANOMIPS_TLS_TPREL12, + BFD_RELOC_NANOMIPS_TLS_TPREL16 + }; + const bfd_reloc_code_real_type dtp_relocs[] = { + BFD_RELOC_NANOMIPS_TLS_DTPREL12, + BFD_RELOC_NANOMIPS_TLS_DTPREL16 + }; + size_t rcount; + const bfd_reloc_code_real_type *rlist; + + switch (rtype) + { + case BFD_RELOC_NANOMIPS_GPREL18: + rlist = gp_relocs; + rcount = ARRAY_SIZE (gp_relocs); + break; + case BFD_RELOC_NANOMIPS_TLS_TPREL12: + rlist = tp_relocs; + rcount = ARRAY_SIZE (tp_relocs); + break; + case BFD_RELOC_NANOMIPS_TLS_DTPREL12: + rlist = dtp_relocs; + rcount = ARRAY_SIZE (dtp_relocs); + break; + default: + rlist = &rtype; + rcount = 1; + break; + } + + for (i = 0; i < rcount; i++) + { + reloc_howto_type *howto; + howto = bfd_reloc_type_lookup (stdoutput, rlist[i]); + + if ((howto->dst_mask & opmask) == howto->dst_mask + && howto->bitsize == operand_base->size) + return true; + } + return false; +} + +/* Generic INT matcher. */ + +static bool +match_int_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_int_operand *operand; + unsigned int uval; + int min_val, max_val, factor; + offsetT sval; + + operand = (const struct nanomips_int_operand *) operand_base; + factor = 1 << operand->shift; + min_val = nanomips_int_operand_min (operand); + max_val = nanomips_int_operand_max (operand); + + if (match_relocatable_int_operand (operand_base)) + { + /* The operand can be relocated. */ + if (!match_expression (arg, &offset_expr, offset_reloc)) + return false; + + if (offset_reloc[0] != BFD_RELOC_UNUSED + && !nanomips_48bit_reloc_p (offset_reloc[0])) + /* Relocation operators were used. Check if relocation + destination mask matches operand bits. */ + { + if (flex_reloc_p (offset_reloc[0])) + return match_flex_reloc_for_int_operand (operand_base, + offset_reloc[0]); + else + { + int opmask; + reloc_howto_type *howto; + + if (offset_reloc[0] == BFD_RELOC_NANOMIPS_LO12 + && (forced_insn_length == 2 + || (forced_insn_format + && insn_length (arg->insn->insn_mo) == 2))) + offset_reloc[0] = BFD_RELOC_NANOMIPS_LO4_S2; + + howto = bfd_reloc_type_lookup (stdoutput, offset_reloc[0]); + opmask = nanomips_operand_mask (operand_base); + /* This is not an direct mask comparison. In some cases + the relocation targets only a part of the operand bits. */ + return ((howto->dst_mask & opmask) == howto->dst_mask); + } + } + + if (offset_expr.X_op != O_constant) + { + /* Accept non-constant operands if no later alternative matches, + leaving it for the caller to process. */ + if (operand_base->size == 16 && offset_expr.X_op == O_subtract) + { + offset_reloc[0] = BFD_RELOC_UNUSED + RT_ADDIU; + return true; + } + else + { + offset_reloc[0] = BFD_RELOC_NANOMIPS_LO12; + return arg->lax_match; + } + } + + /* Clear the global state; we're going to install the operand + ourselves. */ + if (min_val < 0) + sval = (int) offset_expr.X_add_number; + else + sval = offset_expr.X_add_number; + + offset_expr.X_op = O_absent; + + /* For compatibility with older assemblers, we accept + 0x8000-0xffff as signed 16-bit numbers when only + signed numbers are allowed. */ + if (sval > max_val) + { + max_val = ((1 << operand_base->size) - 1) << operand->shift; + if (!arg->lax_match && sval <= max_val) + return false; + } + } + else + { + if (!match_const_int (arg, &sval)) + return false; + if (min_val < 0 && IS_ZEXT_32BIT_NUM (sval)) + sval = (int) sval; + } + + arg->last_op_int = sval; + + if (sval < min_val || sval > max_val || sval % factor) + { + match_out_of_range (arg); + return false; + } + + uval = (unsigned int) sval >> operand->shift; + uval -= operand->bias; + + insn_insert_operand (arg->insn, operand_base, uval); + return true; +} + +/* Match integer operand with no relocation. */ +static bool +match_imm_int_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + if (!match_int_operand (arg, operand_base)) + return false; + + return ((offset_expr.X_op == O_absent || offset_expr.X_op == O_constant) + && *offset_reloc == BFD_RELOC_UNUSED); +} + +#define MAX_ADDI_OFFSET (0xffff) + +#define MIN_ADDI_OFFSET (-4095) + +static bool +match_int_word (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand ATTRIBUTE_UNUSED) +{ + if (match_expression (arg, &offset_expr, offset_reloc)) + { + if (offset_reloc[0] != BFD_RELOC_UNUSED + && !nanomips_48bit_reloc_p (offset_reloc[0]) + && !flex_reloc_p (offset_reloc[0])) + /* Any other relocation operators not allowed in this position. */ + return false; + + if (offset_reloc[0] == BFD_RELOC_UNUSED) + offset_reloc[0] = BFD_RELOC_NANOMIPS_I32; + + /* We don't match symbol-difference expressions here. */ + if (offset_expr.X_op != O_constant && offset_expr.X_op_symbol == NULL) + { + arg->insn->insn_opcode_ext = 0; + return true; + } + else + if ((forced_insn_length == 6 + || forced_insn_format + || offset_expr.X_add_number > MAX_ADDI_OFFSET + || offset_expr.X_add_number < MIN_ADDI_OFFSET) + && offset_expr.X_op != O_big) + /* We don't match the 48-bit instruction, when we could make + do with a 32-bit one, unless explicitly required. */ + { + arg->last_op_int = offset_expr.X_add_number; + arg->insn->insn_opcode_ext = 0; + return true; + } + } + return false; +} + +/* Match an immediate 32-bit value with possible bias. */ +static bool +match_imm_word (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_int_operand *operand; + operand = (const struct nanomips_int_operand *) operand_base; + + if (match_expression (arg, &offset_expr, offset_reloc)) + { + if (offset_expr.X_op != O_constant || *offset_reloc != BFD_RELOC_UNUSED) + return false; + + if (offset_expr.X_op != O_big) + { + int sval = offset_expr.X_add_number - operand->bias; + arg->last_op_int = sval; + arg->insn->insn_opcode_ext = 0; + arg->insn->insn_opcode_ext = (((sval >> 16) & 0xffff) + | (sval << 16)); + offset_expr.X_op = O_absent; + return true; + } + } + return false; +} + +static bool +match_pcrel_word (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand ATTRIBUTE_UNUSED) +{ + if (match_expression (arg, &offset_expr, offset_reloc)) + { + if (offset_reloc[0] != BFD_RELOC_UNUSED + && !nanomips_48bit_reloc_p (offset_reloc[0])) + /* Any other relocation operators not allowed in this position. */ + return false; + + if (offset_reloc[0] == BFD_RELOC_UNUSED) + offset_reloc[0] = BFD_RELOC_NANOMIPS_PC_I32; + + arg->insn->insn_opcode_ext = 0; + return true; + } + return false; +} + +static bool +match_gprel_word (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand ATTRIBUTE_UNUSED) +{ + if (match_expression (arg, &offset_expr, offset_reloc)) + { + if (offset_reloc[0] != BFD_RELOC_UNUSED + && !gprel_i32_reloc_p (offset_reloc[0]) + && (forced_insn_length != 6 || !gprel_reloc_p (offset_reloc[0]))) + /* Any other relocation operators not allowed in this position. */ + return false; + + if (offset_reloc[0] == BFD_RELOC_UNUSED) + offset_reloc[0] = BFD_RELOC_NANOMIPS_GPREL_I32; + + arg->insn->insn_opcode_ext = 0; + return true; + } + return false; +} + +/* OP_MAPPED_INT matcher. */ +static bool +match_mapped_int_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_mapped_int_operand *operand; + unsigned int uval, num_vals; + offsetT sval; + + operand = (const struct nanomips_mapped_int_operand *) operand_base; + if (!match_const_int (arg, &sval)) + return false; + + num_vals = 1 << operand_base->size; + for (uval = 0; uval < num_vals; uval++) + if (operand->int_map[uval] == sval) + break; + if (uval == num_vals) + { + match_out_of_range (arg); + return false; + } + + insn_insert_operand (arg->insn, operand_base, uval); + return true; +} + +/* OP_MSB matcher. */ + +static bool +match_msb_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_msb_operand *operand; + int min_val, max_val, max_high; + offsetT size, sval, high; + + operand = (const struct nanomips_msb_operand *) operand_base; + min_val = operand->bias; + max_val = min_val + (1 << operand_base->size) - 1; + max_high = operand->opsize; + + if (!match_const_int (arg, &size)) + return false; + + high = size + arg->last_op_int; + sval = operand->add_lsb ? high : size; + + if (size < 0 || high > max_high || sval < min_val || sval > max_val) + { + match_out_of_range (arg); + return false; + } + insn_insert_operand (arg->insn, operand_base, sval - min_val); + return true; +} + +/* OP_REG matcher. */ + +static bool +match_reg_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_reg_operand *operand; + unsigned int regno, uval, num_vals; + + operand = (const struct nanomips_reg_operand *) operand_base; + if (!match_reg (arg, operand->reg_type, ®no)) + return false; + + if (operand->reg_map) + { + num_vals = 1 << operand->root.size; + for (uval = 0; uval < num_vals; uval++) + if (operand->reg_map[uval] == regno) + break; + if (num_vals == uval) + return false; + } + else + uval = regno; + + if (operand_base->size > 0 + && uval >= (unsigned int) (1 << operand_base->size)) + { + match_out_of_range (arg); + return false; + } + + arg->last_regno = regno; + if (arg->opnum == 1) + arg->dest_regno = regno; + insn_insert_operand (arg->insn, operand_base, uval); + return true; +} + +/* OP_REG_PAIR matcher. */ + +static bool +match_reg_pair_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_reg_pair_operand *operand; + unsigned int regno1, regno2, uval, num_vals; + + operand = (const struct nanomips_reg_pair_operand *) operand_base; + if (!match_reg (arg, operand->reg_type, ®no1) + || !match_char (arg, ',') + || !match_reg (arg, operand->reg_type, ®no2)) + return false; + + num_vals = 1 << operand_base->size; + for (uval = 0; uval < num_vals; uval++) + if (operand->reg1_map[uval] == regno1 + && operand->reg2_map[uval] == regno2) + break; + if (uval == num_vals) + return false; + + insn_insert_operand (arg->insn, operand_base, uval); + return true; +} + +/* OP_PCREL matcher. The caller chooses the relocation type. */ + +static bool +match_pcrel_operand (struct nanomips_arg_info *arg) +{ + bfd_reloc_code_real_type r[3]; + + return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED; +} + +static bool +match_non_zero_pcrel_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + bfd_reloc_code_real_type r[3]; + + if (!match_expression (arg, &offset_expr, r) || r[0] != BFD_RELOC_UNUSED) + return false; + + if (!offset_expr.X_add_symbol && offset_expr.X_add_number == 0) + { + set_insn_error (arg->argnum, + _("the PC relative offset must not be $0")); + return false; + } + + /* Enforce operand bits in the instruction encoding as non-zero + so that it can be distinguished from another instructions with the same + encoding and 0 in these bit positions. Requires the relocation + scheme to be RELA, which is trivially true only for nanoMIPS. The check + for ISA just to be on the safe side. */ + insn_insert_operand (arg->insn, operand_base, + (1 << operand_base->size) - 1); + return true; +} + +/* OP_HI20_INT matcher. */ + +#define UNSIGNED_OPVALUE(OP) \ +{ { OP_INT, OP->size, OP->lsb, OP->size_top, OP->lsb_top }, \ + ((1 << OP->size) - 1), 0, 0, false } + +#define SIGNEX_OPVALUE(OP) { OP_INT, OP->size - 1, OP->lsb, 0, 0 } + +static bool +match_hi20_int_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base + ATTRIBUTE_UNUSED) +{ + offsetT uval = 0; + const struct nanomips_int_operand op_enc = { + {OP_INT, 20, 2, 1, 0}, (1 << 20) - 1, 12, 0, false + }; + const struct nanomips_int_operand op_shuffle = { + {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 12, 0, false + }; + + if (!match_expression (arg, &offset_expr, offset_reloc)) + return false; + + /* 16-bit immediate without %hi reloc */ + if (offset_reloc[0] == BFD_RELOC_UNUSED && offset_expr.X_op == O_constant) + { + if ((offset_expr.X_add_number & ~0xffff) == 0) + uval = offset_expr.X_add_number << 4; + else + { + match_out_of_range (arg); + return false; + } + } + /* 20-bit high part using %hi */ + else if (hi_reloc_p (offset_reloc[0])) + uval = offset_expr.X_add_number >> 12; + else + { + /* Accept non-constant operands if no later alternative matches, + leaving it for the caller to process. */ + if (arg->lax_match) + offset_reloc[0] = BFD_RELOC_NANOMIPS_HI20; + return arg->lax_match; + } + + if (offset_expr.X_op == O_constant) + { + offset_expr.X_op = O_absent; + /* Re-shuffle and insert lower 19-bits, exluding sign. */ + uval = nanomips_insert_operand (&op_shuffle.root, + uval & 0x80000, uval & 0x7ffff); + + /* Insert 20-bits in to instruction. */ + insn_insert_operand (arg->insn, &op_enc.root, uval); + } + return true; +} + +/* OP_HI20_PCREL matcher. Relocation must be specified. */ +static bool +match_hi20_pcrel_operand (struct nanomips_arg_info *arg) +{ + const struct nanomips_int_operand op_enc = { + {OP_INT, 20, 2, 1, 0 }, (1 << 20) - 1, 0, 0, false + }; + const struct nanomips_int_operand op_shuffle = { + {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 0, 0, false + }; + /* Encode full 20-bit signed value. */ + if (!match_expression (arg, &offset_expr, offset_reloc)) + return false; + + if (offset_reloc[0] == BFD_RELOC_UNUSED + || (hi_reloc_p (offset_reloc[0]) && offset_expr.X_op == O_constant)) + { + unsigned int uval = offset_expr.X_add_number; + + if (hi_reloc_p (offset_reloc[0])) + uval = uval >> 12; + + /* Re-shuffle and insert lower 19-bits, exluding sign. */ + uval = nanomips_insert_operand (&op_shuffle.root, + uval & 0x80000, uval & 0x7ffff); + /* Insert 20-bits in to instruction. */ + insn_insert_operand (arg->insn, &op_enc.root, uval); + /* Expression is completely handled, we don't want append_insn + messing up the opcode. */ + offset_expr.X_op = O_absent; + return true; + } + else + return (offset_reloc[0] == BFD_RELOC_NANOMIPS_PCREL_HI20 + || offset_reloc[0] == BFD_RELOC_NANOMIPS_GOTPC_HI20); +} + +static bool +match_hi20_scaled_operand (struct nanomips_arg_info *arg) +{ + const struct nanomips_int_operand op_enc = { + {OP_INT, 20, 2, 1, 0}, (1 << 20) - 1, 12, 0, false + }; + const struct nanomips_int_operand op_shuffle = { + {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 12, 0, false + }; + offsetT uval = 0; + + if (!match_const_int (arg, &uval) || (uval & 0xfff) != 0) + return false; + + uval = uval >> 12; + offset_expr.X_op = O_absent; + + /* Re-shuffle and insert lower 19-bits, exluding sign. */ + uval = nanomips_insert_operand (&op_shuffle.root, + uval & 0x80000, uval & 0x7ffff); + + /* Insert 20-bits in to instruction. */ + insn_insert_operand (arg->insn, &op_enc.root, uval); + + return true; +} + +/* OP_CHECK_PREV matcher. */ + +static bool +match_check_prev_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + const struct nanomips_check_prev_operand *operand; + unsigned int regno; + + operand = (const struct nanomips_check_prev_operand *) operand_base; + + if (!match_reg (arg, OP_REG_GP, ®no)) + return false; + + if (!operand->zero_ok && regno == 0) + return false; + + if ((operand->less_than_ok && regno < arg->last_regno) + || (operand->greater_than_ok && regno > arg->last_regno) + || (operand->equal_ok && regno == arg->last_regno)) + { + arg->last_regno = regno; + insn_insert_operand (arg->insn, operand_base, regno); + return true; + } + return false; +} + +/* OP_MAPPED_CHECK_PREV matcher. */ + +static bool +match_mapped_check_prev_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + unsigned char uval, last_uval; + const struct nanomips_mapped_check_prev_operand *operand + = (const struct nanomips_mapped_check_prev_operand *) operand_base; + + last_uval = nanomips_encode_reg_operand (operand_base, arg->last_regno); + + if (!match_reg_operand (arg, operand_base)) + return false; + + if (!operand->zero_ok && arg->last_regno == 0) + return false; + + uval = nanomips_encode_reg_operand (operand_base, arg->last_regno); + + if ((operand->less_than_ok && uval < last_uval) + || (operand->greater_than_ok && uval > last_uval) + || (operand->equal_ok && uval == last_uval)) + return true; + + return false; +} + +/* OP_SAVE_RESTORE_FP_LIST matcher for nanoMIPS. */ + +static bool +match_save_restore_fp_list_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + unsigned int opval; + unsigned int regno1, regno2; + + if (!match_reg_range (arg, OP_REG_FP, ®no1, ®no2)) + return false; + + if (regno1 != 0 || regno2 > 15) + return false; + + opval = (regno2 - regno1); + + insn_insert_operand (arg->insn, operand, opval); + return true; +} + +/* OP_SAVE_RESTORE_LIST matcher. */ + +static bool +match_save_restore_list_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + unsigned int opval, count; + unsigned first_reg, last_reg; + bool mode16 = (insn_length (arg->insn->insn_mo) == 2); + bool gp = false; + + count = 0; + first_reg = last_reg = RNUM_MASK + 1; + do + { + unsigned int regno1 = 0, regno2 = 0; + bool first_range = false; + + if (!match_reg_range (arg, OP_REG_GP, ®no1, ®no2)) + { + arg->token--; + if (regno2 != 0 && regno2 < regno1) + set_insn_error_ss (count, + "register range (%s-%s) is not in " + "increasing order", + nanomips_reg_names[32 + regno1].name, + nanomips_reg_names[32 + regno2].name); + return false; + } + + if (regno1 <= 29 && regno2 >= 29) + { + set_insn_error_ss (regno1, + "%s register list should not contain $sp%s", + arg->insn->insn_mo->name, ""); + return false; + } + + if (first_reg == RNUM_MASK + 1) + { + first_reg = last_reg = regno1; + first_range = true; + } + + if ((regno1 != ((first_reg & 0x10) | (last_reg + 1) % 32)) + || first_range) + { + /* a non-contiguous sequence */ + if (regno1 == 28 && !mode16 && (nanomips_opts.ase & ASE_xNMS) != 0) + /* enable GP special casing if possible */ + gp = true; + else if (regno1 == 28 && (nanomips_opts.ase & ASE_xNMS) == 0) + { + set_insn_error_ss (count, + "non-contiguous %s of $gp is not available " + "for nanoMIPS subset%s", + arg->insn->insn_mo->name, ""); + return false; + } + else if (regno1 == 28 && mode16) + { + set_insn_error_ss (count, + "non-contiguous %s of $gp is not available " + "for 16-bit instructions%s", + arg->insn->insn_mo->name, ""); + return false; + } + else if (first_range) + ; + else + { + if (regno1 == 30 && first_reg != 30) + set_insn_error (count, "$fp must be the first register in " + "the list"); + else if (regno1 == 31 && first_reg != 30 && first_reg != 31) + set_insn_error (count, "$ra must be the first register in " + "the list"); + else if (last_reg == 31 && regno1 != 16) + set_insn_error_ss (count + 1, "%s must be followed by %s", + nanomips_reg_names[32 + last_reg].name, + nanomips_reg_names[32 + 16].name); + else + set_insn_error_ss (regno1, + "register list not contiguous between " + "%s and %s", + nanomips_reg_names[32 + last_reg].name, + nanomips_reg_names[32 + regno1].name); + return false; + } + } + + last_reg = regno2; + + /* sequence */ + while (regno1++ <= regno2) + count += 1; + } + while (match_char (arg, ',')); + + /* too few or too many registers in list */ + if (count >= 16) + { + set_insn_error_si (count, + "%s can handle a maximum of 16 register, %d requested", + arg->insn->insn_mo->name, count); + return false; + } + + if (mode16 && first_reg < 30) + { + set_insn_error_ss (count + 1, + "first register for 16-bit %s must be $fp or $ra%s", + arg->insn->insn_mo->name, ""); + return false; + } + + if (count * (GPR_SIZE / 8) > arg->last_op_int) + as_warn ("frame too small for save/restore register list"); + + if (mode16) + opval = ((first_reg & 0x1) << 4) | count; + else + opval = ((first_reg << 6) | (count << 1) | gp); + + /* Finally build the instruction. */ + insn_insert_operand (arg->insn, operand, opval); + + return true; +} + +/* OP_NON_ZERO_REG matcher. */ + +static bool +match_non_zero_reg_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + unsigned int regno; + + if (!match_reg (arg, OP_REG_GP, ®no)) + return false; + + if (regno == 0) + { + const unsigned long pinfo = arg->insn->insn_mo->pinfo; + unsigned int regtype; + const char *regstr[] = { "source", "target" }; + + if ((arg->opnum == 1 && (pinfo & INSN_WRITE_1) != 0) + || (arg->opnum == 2 && (pinfo & INSN_WRITE_2) != 0)) + regtype = 1; + else + regtype = 0; + + set_insn_error_ss (arg->argnum, _("the %s register must not be $0%s"), + regstr[regtype], ""); + return false; + } + + arg->last_regno = regno; + insn_insert_operand (arg->insn, operand, regno); + return true; +} + +/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the + register that we need to match. */ + +static bool +match_tied_reg_operand (struct nanomips_arg_info *arg, + unsigned int other_regno) +{ + unsigned int regno; + + return match_reg (arg, OP_REG_GP, ®no) && regno == other_regno; +} + +/* OP_BASE_CHECK_OFFSET matcher. */ + +static bool +match_base_checked_offset_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand_base) +{ + unsigned int regno; + const struct nanomips_base_check_offset_operand *operand + = (const struct nanomips_base_check_offset_operand *) operand_base; + + if (!match_reg (arg, OP_REG_GP, ®no)) + return false; + + if ((operand->const_ok + && (offset_expr.X_op == O_constant || offset_expr.X_op == O_absent)) + || (operand->expr_ok + && (offset_expr.X_op == O_symbol || offset_expr.X_op == O_absent)) + || (regno == 0) + || lo_reloc_p (offset_reloc[0])) + { + arg->last_regno = regno; + insn_insert_operand (arg->insn, operand_base, regno); + return true; + } + return false; +} + +/* Read a floating-point constant from S for LI.S or LI.D. LENGTH is + the length of the value in bytes (4 for float, 8 for double) and + USING_GPRS says whether the destination is a GPR rather than an FPR. + + Return the constant in IMM and OFFSET as follows: + + - If the constant should be loaded via memory, set IMM to O_absent and + OFFSET to the memory address. + + - Otherwise, if the constant should be loaded into two 32-bit registers, + set IMM to the O_constant to load into the high register and OFFSET + to the corresponding value for the low register. + + - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent. + + These constants only appear as the last operand in an instruction, + and every instruction that accepts them in any variant accepts them + in all variants. This means we don't have to worry about backing out + any changes if the instruction does not match. We just match + unconditionally and report an error if the constant is invalid. */ + +static bool +match_float_constant (struct nanomips_arg_info *arg, expressionS *imm, + expressionS *offset, unsigned int length, + bool using_gprs) +{ + char *p; + segT seg; + subsegT subseg; + unsigned char *data; + + /* Where the constant is placed is based on how the nanoMIPS assembler + does things: + + length == 4 && using_gprs -- immediate value only + length == 8 && using_gprs -- .rdata or immediate value + length <= -Gnum -- .sdata + length >= -Gnum -- .rodata + + The .sdata sections are only used if permitted by the -G argument. */ + if (arg->token->type != OT_FLOAT) + { + set_insn_error (arg->argnum, _("floating-point expression required")); + return false; + } + + gas_assert (arg->token->u.flt.length == (int) length); + data = arg->token->u.flt.data; + ++arg->token; + + /* Handle 32-bit constants -- immediate value is best. */ + if (length == 4 + && !nanomips_disable_float_construction) + { + imm->X_op = O_constant; + if (!target_big_endian) + imm->X_add_number = bfd_getl32 (data); + else + imm->X_add_number = bfd_getb32 (data); + offset->X_op = O_absent; + return true; + } + + /* Handle 64-bit constants for which an immediate value is best. */ + if (length == 8 + && !nanomips_disable_float_construction + && ((data[0] == 0 && data[1] == 0) + || (data[2] == 0 && data[3] == 0) + || ((nanomips_opts.ase & ASE_xNMS) != 0 && using_gprs)) + && ((data[4] == 0 && data[5] == 0) + || (data[6] == 0 && data[7] == 0) + || ((nanomips_opts.ase & ASE_xNMS) != 0 && using_gprs))) + { + /* The value is simple enough to load with a couple of instructions. + If using 32-bit registers, set IMM to the high order 32 bits and + OFFSET to the low order 32 bits. Otherwise, set IMM to the entire + 64 bit constant. */ + if (GPR_SIZE == 32) + { + imm->X_op = O_constant; + offset->X_op = O_constant; + if (!target_big_endian) + { + imm->X_add_number = bfd_getl32 (data + 4); + offset->X_add_number = bfd_getl32 (data); + } + else + { + imm->X_add_number = bfd_getb32 (data); + offset->X_add_number = bfd_getb32 (data + 4); + } + if (offset->X_add_number == 0) + offset->X_op = O_absent; + } + else + { + imm->X_op = O_constant; + if (!target_big_endian) + imm->X_add_number = bfd_getl64 (data); + else + imm->X_add_number = bfd_getb64 (data); + offset->X_op = O_absent; + } + return true; + } + + /* Switch to the right section. */ + seg = now_seg; + subseg = now_subseg; + + if (!using_gprs && g_switch_value >= length && nanomips_opts.pic == NO_PIC) + s_change_sec ('s'); + else + s_change_sec ('r'); + + if (seg == now_seg) + as_bad (_("cannot use `%s' in this section"), arg->insn->insn_mo->name); + + /* Set the argument to the current address in the section. */ + imm->X_op = O_absent; + offset->X_op = O_symbol; + offset->X_add_symbol = symbol_temp_new_now (); + offset->X_add_number = 0; + + /* Put the floating point number into the section. */ + p = frag_more (length); + memcpy (p, data, length); + + /* Switch back to the original section. */ + subseg_set (seg, subseg); + return true; +} + +/* Copy bits from one part of an instruction to another part. + Source bits are specified by size & lsb. Destination bits + are specified by size_top and lsb_top. */ +static bool +match_copy_bits (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + const struct nanomips_operand op_src = { + OP_INT, operand->size, operand->lsb, 0, 0 + }; + const struct nanomips_operand op_dest = { + OP_INT, operand->size_top, operand->lsb_top, 0, 0 + }; + unsigned int uval; + + uval = nanomips_extract_operand (&op_src, arg->insn->insn_opcode); + insn_insert_operand (arg->insn, &op_dest, uval); + return true; +} + +/* Select value for a named COP0 register. */ +static bool +match_cp0_select (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + offsetT uval; + + /* Expect an immediate select value between 0 & 31. */ + if (!match_const_int (arg, &uval) + || uval < 0 + || uval >= (1 << NANOMIPSOP_SH_CP0SEL)) + return false; + + /* Check if this select value is allowed by the saved mask of the last + seen CP0 register. */ + if (((1 << uval) & arg->select_mask) == 0) + return false; + + insn_insert_operand (arg->insn, operand, uval); + return true; +} + +/* S is the text seen for ARG. Match it against OPERAND. Return the end + of the argument text if the match is successful, otherwise return null. */ + +static bool +match_operand (struct nanomips_arg_info *arg, + const struct nanomips_operand *operand) +{ + switch (operand->type) + { + case OP_INT: + return match_int_operand (arg, operand); + + case OP_MAPPED_INT: + return match_mapped_int_operand (arg, operand); + + case OP_MSB: + return match_msb_operand (arg, operand); + + case OP_REG: + case OP_OPTIONAL_REG: + return match_reg_operand (arg, operand); + + case OP_REG_PAIR: + return match_reg_pair_operand (arg, operand); + + case OP_PCREL: + return match_pcrel_operand (arg); + + case OP_SAVE_RESTORE_LIST: + return match_save_restore_list_operand (arg, operand); + + case OP_SAVE_RESTORE_FP_LIST: + return match_save_restore_fp_list_operand (arg, operand); + + case OP_REPEAT_DEST_REG: + return match_tied_reg_operand (arg, arg->dest_regno); + + case OP_REPEAT_PREV_REG: + return match_tied_reg_operand (arg, arg->last_regno); + + case OP_CHECK_PREV: + return match_check_prev_operand (arg, operand); + + case OP_NON_ZERO_REG: + return match_non_zero_reg_operand (arg, operand); + + case OP_NON_ZERO_PCREL_S1: + return match_non_zero_pcrel_operand (arg, operand); + + case OP_HI20_PCREL: + return match_hi20_pcrel_operand (arg); + + case OP_HI20_INT: + return match_hi20_int_operand (arg, operand); + + case OP_HI20_SCALE: + return match_hi20_scaled_operand (arg); + + case OP_MAPPED_CHECK_PREV: + return match_mapped_check_prev_operand (arg, operand); + + case OP_UINT_WORD: + case OP_INT_WORD: + return match_int_word (arg, operand); + + case OP_PC_WORD: + return match_pcrel_word (arg, operand); + + case OP_GPREL_WORD: + return match_gprel_word (arg, operand); + + case OP_DONT_CARE: + return false; + + case OP_NEG_INT: + return match_negative_int_operand (arg, operand); + + case OP_IMM_INT: + return match_imm_int_operand (arg, operand); + + case OP_IMM_WORD: + return match_imm_word (arg, operand); + + case OP_BASE_CHECK_OFFSET: + return match_base_checked_offset_operand (arg, operand); + + case OP_COPY_BITS: + return match_copy_bits (arg, operand); + + case OP_CP0SEL: + return match_cp0_select (arg, operand); + + default: + abort (); + } +} + +#define INSN_ERET 0x42000018 +#define INSN_DERET 0x4200001f +#define INSN_DMULT 0x1c +#define INSN_DMULTU 0x1d + +#define BASE_REG_EQ(INSN1, INSN2) \ + ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \ + == (((INSN2) >> OP_SH_RS) & OP_MASK_RS)) + +/* For nanoMIPS macros, we need to generate a local number label + as the target of branches. */ +#define NANOMIPS_LABEL_CHAR '\037' +static unsigned long nanomips_target_label; +static char nanomips_target_name[32]; + +static char * +nanomips_label_name (void) +{ + char *p = nanomips_target_name; + char symbol_name_temporary[24]; + unsigned long l; + int i; + + if (*p) + return p; + + i = 0; + l = nanomips_target_label; +#ifdef LOCAL_LABEL_PREFIX + *p++ = LOCAL_LABEL_PREFIX; +#endif + *p++ = 'L'; + *p++ = NANOMIPS_LABEL_CHAR; + do + { + symbol_name_temporary[i++] = l % 10 + '0'; + l /= 10; + } + while (l != 0); + while (i > 0) + *p++ = symbol_name_temporary[--i]; + *p = '\0'; + + return nanomips_target_name; +} + +static void +nanomips_label_expr (expressionS *label_expr) +{ + label_expr->X_op = O_symbol; + label_expr->X_add_symbol = symbol_find_or_make (nanomips_label_name ()); + label_expr->X_add_number = 0; +} + +static void +nanomips_label_inc (void) +{ + nanomips_target_label++; + *nanomips_target_name = '\0'; +} + +static void +nanomips_add_label (void) +{ + colon (nanomips_label_name ()); + nanomips_label_inc (); +} + +/* If assembling nanoMIPS code, then return the nanoMIPS + reloc corresponding to the requested one if any. Otherwise + return the reloc unchanged. +*/ + +static bfd_reloc_code_real_type +nanomips_map_reloc (bfd_reloc_code_real_type reloc) +{ + static const bfd_reloc_code_real_type relocs[][2] = { + /* Keep sorted incrementally by the left-hand key. */ + {BFD_RELOC_GPREL16, BFD_RELOC_NANOMIPS_GPREL18}, + {BFD_RELOC_HI16, BFD_RELOC_NANOMIPS_HI20}, + {BFD_RELOC_HI16_S, BFD_RELOC_NANOMIPS_HI20}, + {BFD_RELOC_LO16, BFD_RELOC_NANOMIPS_LO12}, + }; + bfd_reloc_code_real_type r; + size_t i; + + for (i = 0; i < ARRAY_SIZE (relocs); i++) + { + r = relocs[i][0]; + if (r > reloc) + return reloc; + if (r == reloc) + return relocs[i][1]; + } + return reloc; +} + +/* Remove first call from call-list and decrement numcalls. */ +static void +stublist_pop_call (struct balc_stub *stub) +{ + struct call_list *ptr = stub->first_call; + + if (ptr) + { + if (stub->last_call == ptr) + stub->last_call = NULL; + + stub->first_call = ptr->next; + xfree (ptr); + stub->numcalls -= 1; + } + return; +} + +/* Remove all calls greater than or equal to CALLSITE + from call-list and decrement numcalls. LIST is guaranteed + to have at least 2 elements when this is called. */ +static void +stublist_trunc_calls (struct balc_stub *stub, bfd_vma callsite) +{ + struct call_list *list = stub->first_call; + + while (list && list->next) + { + if (list->next->callsite < callsite) + list = list->next; + else + break; + } + + if (list) + { + stub->last_call = list; + list = list->next; + stub->last_call->next = NULL; + while (list) + { + struct call_list *ptr = list; + list = list->next; + stub->numcalls -= 1; + xfree (ptr); + } + } + return; +} + +/* Add call to call-list. */ +static void +stublist_append_call (struct balc_stub *stub, bfd_vma callsite) +{ + struct call_list *next + = (struct call_list *) xmalloc (sizeof (struct call_list)); + memset (next, 0, sizeof (struct call_list)); + next->callsite = callsite; + + if (stub->first_call == NULL) + stub->first_call = stub->last_call = next; + else if (callsite < stub->first_call->callsite) + { + next->next = stub->first_call; + stub->first_call = next; + } + else if (callsite > stub->last_call->callsite) + { + stub->last_call->next = next; + stub->last_call = next; + } + else + { + struct call_list *list = stub->first_call; + + while (list && list->next) + { + if (list->next->callsite < callsite) + list = list->next; + else + break; + } + + next->next = list->next; + list->next = next; + } + + stub->numcalls += 1; + return; +} + +static void +stublist_merge_forward_calls (struct balc_stub *dest, struct balc_stub *src) +{ + src->last_call->next = dest->first_call; + dest->first_call = src->first_call; + if (dest->last_call == NULL) + dest->last_call = dest->first_call; + dest->numcalls += src->numcalls; +} + +static void +stublist_merge_backward_calls (struct balc_stub *dest, struct balc_stub *src) +{ + dest->last_call->next = src->first_call; + dest->last_call = src->last_call; + dest->numcalls += src->numcalls; +} + +/* FIXME: Using a hash-table as a stubtable for now. We ideally want + these to be sorted on first_offset, so a different data structure + is required to get the best possible results. */ +#define stubtable_find str_hash_find +#define stubtable_create str_htab_create +#define stubtable_insert str_hash_insert +#define stubtable_delete str_hash_delete +#define stubtable_traverse htab_traverse + +static bool +balc_in_stub_range (const bfd_vma callsite, const stub_group *stubg) +{ + fragS *fragp; + bfd_vma minaddr; + + if (stubg == NULL + || stubg->fragp == NULL + || stubg->fragp->fr_address + stubg->fragp->fr_fix == 0) + return false; + + fragp = stubg->fragp; + + /* 10-bit left shifted +ve offset taken from NextPC + gives a range of 1024 from current call-site. */ + if (fragp->fr_address + fragp->fr_fix >= 1024) + minaddr = (fragp->fr_address + fragp->fr_fix - 1024); + else + minaddr = 0; + + /* Only the lowest address of the stub needs to be reachable. */ + if (stubg->next_offset != 0) + minaddr += (stubg->next_offset - 4); + + /* 10-bit left shifted -ve offset taken from NextPC + gives a range of 1022 from the current call-site. */ + if (callsite > (fragp->fr_address + fragp->fr_fix + 1022) + || callsite < minaddr) + return false; + + return true; +} + +static bool +balc_in_stub_group (const char *func, const bfd_vma callsite, + const stub_group *stubg, struct balc_stub **stub) +{ + gas_assert (func != NULL && stubg != NULL); + + if (!balc_in_stub_range (callsite, stubg) || (stubg->stubtable == NULL)) + return false; + + *stub = stubtable_find (stubg->stubtable, func); + return (*stub != NULL); +} + +static bool +balc_add_stub (symbolS *sym, stub_group *stubg) +{ + struct balc_stub *stub = NULL; + const char *func = S_GET_NAME (sym); + + gas_assert (func != NULL && stubg != NULL); + + if (!stubg->stubtable) + stubg->stubtable = stubtable_create (); + + stub = (struct balc_stub *) stubtable_find (stubg->stubtable, func); + + if (stub == NULL) + { + stub = (struct balc_stub *) xmalloc (sizeof (struct balc_stub)); + + if (stub == NULL) + return false; + + memset (stub, 0, sizeof (struct balc_stub)); + stub->sym = sym; + } + return true; +} + +/* Consolidate this stub with a preceding or succeeding stub if all + calls to this stub can reach the alternative just as well. */ +static bool +balc_merge_stub (const char *func, stub_group *stubg, + struct balc_stub **stub_ptr_ptr) +{ + stub_group *next = stubg->next; + stub_group *prev = stubg->prev; + struct balc_stub *prevstub = NULL; + struct balc_stub *nextstub = NULL; + struct balc_stub *stub = *stub_ptr_ptr; + bfd_vma callsite = stub->first_call->callsite; + + /* Correction to account for the fact that the 4-byte stub will also move + forward from its current position to end of target stub-group. */ + if (stub->fragp && RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype)) + callsite += 4; + + /* Any forward stub that we find can be the target for merge. */ + while (next && next->stubtable && balc_in_stub_range (callsite, next)) + { + nextstub = (struct balc_stub *) stubtable_find (next->stubtable, func); + + if (nextstub) + break; + next = next->next; + } + + callsite = stub->last_call->callsite; + + /* Correction to account for the fact that the 4-byte stub will also move + backward from its current position to end of target stub-group. */ + if (stub->fragp && RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype) + && callsite > stub->fragp->fr_address) + callsite -= 4; + + /* A previous stub can be a target only if it already has calls. */ + while (prev && prev->stubtable && balc_in_stub_range (callsite, prev)) + { + struct balc_stub *pstub + = (struct balc_stub *) stubtable_find (prev->stubtable, func); + + if (pstub && pstub->numcalls) + { + prevstub = pstub; + break; + } + prev = prev->prev; + } + + /* If we have 2 candidates, then we favour the preceding one only if it + already has more calls than the succeeding one. */ + if (prevstub) + { + if (nextstub && (nextstub->numcalls >= prevstub->numcalls)) + { + stublist_merge_forward_calls (nextstub, stub); + *stub_ptr_ptr = nextstub; + } + else + { + stublist_merge_backward_calls (prevstub, stub); + *stub_ptr_ptr = prevstub; + } + stubtable_delete (stubg->stubtable, func); + return true; + } + else if (nextstub) + { + stublist_merge_forward_calls (nextstub, stub); + *stub_ptr_ptr = nextstub; + stubtable_delete (stubg->stubtable, func); + return true; + } + else + return false; +} + +/* hash traverse - add stub for each function to current stub-group. */ +static void +balc_add_traverse (const char *key, void *value) +{ + struct balc_stub *stub = (struct balc_stub *) value; + + if (stub->fragp != NULL) + { + symbolS *sym = stub->fragp->fr_symbol; + gas_assert (strcmp (key, S_GET_NAME (sym)) == 0); + balc_add_stub (sym, stubg_now); + } + return; +} + +/* hash traverse - create a frag stub for each function. */ +static void +balc_frag_traverse (const char *key ATTRIBUTE_UNUSED, void *value) +{ + struct balc_stub *stub = (struct balc_stub *) value; + symbolS *l; + fragS *old_frag = stub->fragp; + + if (old_frag) + { + old_frag->fr_subtype + = RELAX_MD_CLEAR_KEEPSTUB (old_frag->fr_subtype); + stub->sym = old_frag->fr_symbol; + } + + frag_grow (4); + l = symbol_new (nanomips_label_name (), now_seg, frag_now, frag_now->fr_fix); + nanomips_label_inc (); + stub->fragp = frag_now; + add_relaxed_insn (&nanomips_bc32_insn, 4, 0, + RELAX_MD_ENCODE (RT_BALC_STUB, 0, 1), + stub->sym, 0); + stub->sym = l; + if (stub->numcalls >= 3) + stub->fragp->fr_subtype + = RELAX_MD_MARK_KEEPSTUB (stub->fragp->fr_subtype); + if (stubg_now->fragp == NULL) + stubg_now->fragp = stub->fragp; + + return; +} + +/* Find an elligible stub for a function call. */ +static bool +balc_find_stub_inrange (const char *func, const bfd_vma callsite, + stub_group *stubg, struct balc_stub **stub) +{ + stub_group *plink; + stub_group *nlink; + struct balc_stub *pstub = NULL; + struct balc_stub *nstub = NULL; + + /* Search backwards within range. */ + plink = stubg->prev; + while (plink != NULL + && !balc_in_stub_group (func, callsite, plink, &pstub) + && balc_in_stub_range (callsite, plink)) + plink = plink->prev; + + /* Search forwards within range. */ + nlink = stubg; + while (nlink != NULL + && !balc_in_stub_group (func, callsite, nlink, &nstub) + && balc_in_stub_range (callsite, nlink)) + nlink = nlink->next; + + /* When 2 possible stub candidates are available, choose the preceeding one + only if it already has more associated calls than the suceeding one. */ + if (pstub) + { + if (nstub && (pstub->numcalls <= nstub->numcalls)) + *stub = nstub; + else + *stub = pstub; + + return true; + } + else if (nstub) + { + *stub = nstub; + return true; + } + return false; +} + +/* Find closest elligible stub for this function call. */ +static bool +balc_get_stub_for_symbol (const char *func, const bfd_vma callsite, + stub_group *stubg, symbolS **symbol) +{ + stub_group *plink = stubg->prev; + stub_group *nlink = stubg; + struct balc_stub *stub; + + while (plink != NULL || nlink != NULL) + { + if (((nlink && balc_in_stub_group (func, callsite, nlink, &stub)) + || (plink && balc_in_stub_group (func, callsite, plink, &stub))) + && stub->numcalls >= 3) + { + *symbol = stub->sym; + return true; + } + + if (plink != NULL) + plink = plink->prev; + if (nlink != NULL) + nlink = nlink->next; + } + return false; +} + +/* Initialize new stub-group at entry to a function. */ +static void +stubgroup_new (asection *sec) +{ + stub_group *stubg = NULL; + + /* Create the master table. */ + if (balc_stubgroup_table == NULL) + balc_stubgroup_table = str_htab_create (); + + if (stubg_now) + { + if (stubg_now->seg != sec) + { + /* Change of sections, find the end of correct list. */ + stubg_now = str_hash_find (balc_stubgroup_table, sec->name); + while (stubg_now && stubg_now->next) + stubg_now = stubg_now->next; + } + + /* Re-cycle the previous stubgroup, if unused. */ + if (stubg_now && stubg_now->stubtable == NULL) + stubg = stubg_now; + } + + if (stubg == NULL) + { + stubg = (stub_group *) xmalloc (sizeof (stub_group)); + memset (stubg, 0, sizeof (stub_group)); + stubg->seg = sec; + } + + if (stubg_now == NULL) + /* First stubgroup for this section, add to master table. */ + str_hash_insert (balc_stubgroup_table, sec->name, stubg, 0); + else if (stubg_now != stubg) + { + /* Link in with existing stub-groups for this section */ + stubg_now->next = stubg; + stubg->prev = stubg_now; + } + stubg_now = stubg; +} + +/* Finalize the stub-group at the end of each function. */ +static void +stubgroup_wane (void) +{ + frag_wane (frag_now); + frag_new (0); + /* Carry forward stubs for all previous function calls within + this section. */ + if (stubg_now != NULL + && stubg_now->prev != NULL + && stubg_now->prev->stubtable != NULL + && stubg_now->seg == now_seg) + stubtable_traverse (stubg_now->prev->stubtable, balc_add_traverse, 0); + + /* Create a frag for each stub - this must be done now and for *all* + possible called functions, because frags can not be inserted in a + position retroactively. This is also the reason why we include all + previously called functions within the section - to allow for forward + consolidation of stubs, knowing that many of these stubs will eventually + not be instantiated, due to either paucity of calls or consolidation. */ + if (stubg_now->stubtable && stubg_now->seg == now_seg) + stubtable_traverse (stubg_now->stubtable, balc_frag_traverse, 0); +} + +/* Check whether the last encountered label carried an + explicit relocation. */ + +static bool +explicit_reloc_label_p (fragS *fragP, asection *sec) +{ + if (reloc_list != NULL + && symbol_get_frag (reloc_list->u.a.offset_sym) == fragP) + { + segment_info_type *si = seg_info (sec); + + struct insn_label_list *iter = si->label_list; + while (iter != NULL) + { + if (iter->label == reloc_list->u.a.offset_sym) + return true; + iter = iter->next; + } + } + return false; +} + +/* Check whether the last encountered label carried an explicit + relocation of the specified type. */ + +static bool +explicit_reloc_type_p (fragS *fragP, asection *sec, + unsigned rtype) +{ + return (explicit_reloc_label_p (fragP, sec) + && reloc_list->u.a.howto->type == rtype); +} + +/* Output an instruction. IP is the instruction information. + ADDRESS_EXPR is an operand of the instruction to be used with + RELOC_TYPE. EXPANSIONP is true if the instruction is part of + a macro expansion. */ + +static void +append_insn (struct nanomips_cl_insn *ip, expressionS *address_expr, + bfd_reloc_code_real_type *reloc_type, + bool expansionp ATTRIBUTE_UNUSED) +{ + unsigned long pinfo; + + pinfo = ip->insn_mo->pinfo; + + if (address_expr == NULL) + ip->complete_p = 1; + else if (reloc_type[0] <= BFD_RELOC_UNUSED + && reloc_type[1] == BFD_RELOC_UNUSED + && reloc_type[2] == BFD_RELOC_UNUSED + && address_expr->X_op == O_constant) + { + switch (*reloc_type) + { + case BFD_RELOC_NANOMIPS_HI20: + case BFD_RELOC_NANOMIPS_GPREL_HI20: + ip->insn_opcode |= ((((address_expr->X_add_number >> 12) & 0x1ff) + << 12) + | (((address_expr->X_add_number >> 21) & 0x3ff) + << 2) + | ((address_expr->X_add_number >> 31) & 1)); + ip->complete_p = 1; + break; + + case BFD_RELOC_NANOMIPS_LO12: + case BFD_RELOC_NANOMIPS_GOT_LO12: + ip->insn_opcode |= address_expr->X_add_number & 0xfff; + ip->complete_p = 1; + break; + + case BFD_RELOC_NANOMIPS_IMM16: + ip->insn_opcode |= address_expr->X_add_number & 0xffff; + ip->complete_p = 1; + break; + + case BFD_RELOC_NANOMIPS_NEG12: + ip->insn_opcode |= (-address_expr->X_add_number) & 0xfff; + ip->complete_p = 1; + break; + + case BFD_RELOC_NANOMIPS_I32: + case BFD_RELOC_NANOMIPS_GPREL_I32: + ip->insn_opcode_ext = (((address_expr->X_add_number >> 16) & 0xffff) + | (address_expr->X_add_number << 16)); + ip->complete_p = 1; + break; + + case BFD_RELOC_NANOMIPS_GOT_CALL: + ip->complete_p = 1; + case BFD_RELOC_NANOMIPS_PC_I32: + case BFD_RELOC_NANOMIPS_GOTPC_I32: + break; + + case BFD_RELOC_NANOMIPS_SIGNED_9: + ip->insn_opcode |= ((address_expr->X_add_number & 0xff) + | ((address_expr->X_add_number & 0x100) << 7)); + ip->complete_p = 1; + break; + + default: + break; + } + } + + dwarf2_emit_insn (0); + + if (address_expr + && *reloc_type >= BFD_RELOC_UNUSED + RT_BRANCH_UCND + && !forced_insn_format) + { + int type = *reloc_type - BFD_RELOC_UNUSED; + int al = pinfo & INSN_WRITE_GPR_31; + + gas_assert (address_expr != NULL); + gas_assert (!nanomips_relax.sequence); + + add_relaxed_insn (ip, 4, 2, + RELAX_MD_ENCODE (type, al, 0), + address_expr->X_add_symbol, + address_expr->X_add_number); + /* Track this call for balcp-to-stub relaxation. */ + if (!nanomips_opts.no_balc_stubs + && stubg_now != NULL + && type == RT_BRANCH_UCND + && (ip->insn_mo->pinfo & INSN_WRITE_GPR_31) != 0) + balc_add_stub (address_expr->X_add_symbol, stubg_now); + + *reloc_type = BFD_RELOC_UNUSED; + } + else if (address_expr + && *reloc_type == BFD_RELOC_UNUSED + RT_ADDIU + && address_expr->X_op == O_subtract + && !forced_insn_format) + { + int type = RT_ADDIU; + bool fixed = (nanomips_opts.insn32 || (forced_insn_length == 4)); + + gas_assert (address_expr != NULL); + gas_assert (!nanomips_relax.sequence); + add_relaxed_insn (ip, 6, 4, + RELAX_MD_ENCODE (type, 0, fixed), + make_expr_symbol (address_expr), 0); + *reloc_type = BFD_RELOC_UNUSED; + } + else if (address_expr + && *reloc_type >= BFD_RELOC_UNUSED + RT_BRANCH_UCND + && forced_insn_format) + { + const bfd_reloc_code_real_type rtype[] = + { BFD_RELOC_NANOMIPS_10_PCREL_S1, + BFD_RELOC_NANOMIPS_7_PCREL_S1, + BFD_RELOC_NANOMIPS_4_PCREL_S1 }; + *reloc_type = rtype[*reloc_type - BFD_RELOC_UNUSED - RT_BRANCH_UCND]; + add_fixed_insn (ip); + } + else + { + if (nanomips_relax.sequence) + { + /* If we've reached the end of this frag, turn it into a variant + frag and record the information for the instructions we've + written so far. */ + if (frag_room () < insn_length (ip->insn_mo)) + relax_close_frag (); + nanomips_relax.sizes[nanomips_relax.sequence - 1] += + insn_length (ip->insn_mo); + } + + if (nanomips_relax.sequence != 2) + { + if (nanomips_macro_warning.first_insn_sizes[0] == 0) + nanomips_macro_warning.first_insn_sizes[0] = + insn_length (ip->insn_mo); + nanomips_macro_warning.sizes[0] += insn_length (ip->insn_mo); + nanomips_macro_warning.insns[0]++; + } + if (nanomips_relax.sequence != 1) + { + if (nanomips_macro_warning.first_insn_sizes[1] == 0) + nanomips_macro_warning.first_insn_sizes[1] = + insn_length (ip->insn_mo); + nanomips_macro_warning.sizes[1] += insn_length (ip->insn_mo); + nanomips_macro_warning.insns[1]++; + } + add_fixed_insn (ip); + } + + if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED) + { + bfd_reloc_code_real_type final_type[3]; + reloc_howto_type *howto0; + reloc_howto_type *howto; + int i; + unsigned where; + + /* Perform any necessary conversion to nanoMIPS relocations + and find out how many relocations there actually are. */ + for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++) + final_type[i] = nanomips_map_reloc (reloc_type[i]); + + /* In a compound relocation, it is the final (outermost) + operator that determines the relocated field. */ + howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]); + if (!howto) + abort (); + + if (i > 1) + howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]); + + where = ip->where; + /* For 48-bit nanoMIPS instructions, we want the relocation + to be on the lower 32-bits of the instruction. */ + if (nanomips_48bit_reloc_p (final_type[0])) + where = ip->where + 2; + + ip->fixp[0] = fix_new_exp (ip->frag, where, + bfd_get_reloc_size (howto), + address_expr, + howto0 && howto0->pc_relative, + final_type[0]); + /* Remember the first fix-up in this frag. */ + if (ip->frag->tc_frag_data == NULL) + ip->frag->tc_frag_data = ip->fixp[0]; + + if (nanomips_relax.sequence) + { + if (nanomips_relax.first_fixup == 0) + nanomips_relax.first_fixup = ip->fixp[0]; + } + + /* Add fixups for the second and third relocations, if given. + Note that the ABI allows the second relocation to be + against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the + moment we only use RSS_UNDEF, but we could add support + for the others if it ever becomes necessary. */ + for (i = 1; i < 3; i++) + if (reloc_type[i] != BFD_RELOC_UNUSED) + { + ip->fixp[i] = fix_new (ip->frag, where, + ip->fixp[0]->fx_size, NULL, 0, + false, final_type[i]); + + /* Use fx_tcbit to mark compound relocs. */ + ip->fixp[0]->fx_tcbit = 1; + ip->fixp[i]->fx_tcbit = 1; + } + + if (nanomips_linkrelax_p && (insn_length (ip->insn_mo) != 6)) + { + if (forced_insn_format) + fix_new (ip->frag, ip->where, 0, &abs_symbol, 0, false, + BFD_RELOC_NANOMIPS_FIXED); + else if (forced_insn_length > 0) + fix_new (ip->frag, ip->where, 0, &abs_symbol, 0, false, + (forced_insn_length == 2 ? BFD_RELOC_NANOMIPS_INSN16 + : BFD_RELOC_NANOMIPS_INSN32)); + } + } + + install_insn (ip); + insert_into_history (ip); + + /* If we have just completed an unconditional branch, clear the history. */ + if ((compact_branch_p (&history) && uncond_branch_p (&history)) + && !(history.insn_mo->pinfo2 & INSN2_CONVERTED_TO_COMPACT)) + nanomips_flush_pending_output (); + + /* We just output an insn, so the next one doesn't have a label. */ + nanomips_clear_insn_labels (); +} + +/* Forget that there was any previous instruction or label. + When BRANCH is true, the branch history is also flushed. */ + +void +nanomips_flush_pending_output (void) +{ + prev_nop_frag = NULL; + insert_into_history (NOP_INSN); + nanomips_clear_insn_labels (); +} + +/* Start a (possibly nested) noreorder block. */ + +static void +start_noreorder (void) +{ + nanomips_opts.noreorder++; +} + +/* End a nested noreorder block. */ + +static void +end_noreorder (void) +{ + nanomips_opts.noreorder--; +} + +/* Sign-extend 32-bit mode constants that have bit 31 set and all + higher bits unset. */ + +static void +normalize_constant_expr (expressionS *ex) +{ + if (ex->X_op == O_constant && IS_ZEXT_32BIT_NUM (ex->X_add_number)) + ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000) + - 0x80000000); +} + +/* Sign-extend 32-bit mode address offsets that have bit 31 set and + all higher bits unset. */ + +static void +normalize_address_expr (expressionS *ex) +{ + if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES) + || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS)) + && IS_ZEXT_32BIT_NUM (ex->X_add_number)) + ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000) + - 0x80000000); +} + +struct gprel_insn_match +{ + /* Instruction. */ + const char *str; + /* Arch word size 32/64 if it matters, else 0. */ + int gpwidth; + /* Relocation. */ + bfd_reloc_code_real_type reloc; +}; + +static const struct gprel_insn_match nanomips_gprel_map[] = { + {"lw[gp]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"sw[gp]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"lh[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1}, + {"lhu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1}, + {"sh[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1}, + {"lb[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18}, + {"lbu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18}, + {"sb[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18}, + {"addiu.b", 0, BFD_RELOC_NANOMIPS_GPREL18}, + {"addiu[gp.b]", 0, BFD_RELOC_NANOMIPS_GPREL18}, + {"addiu.w", 0, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"addiu[gp.w]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"ldc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"sdc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"l.d[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"s.d[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"swc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"lwc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"s.s[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"l.s[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"lwu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2}, + {"ld[gp]", 32, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"sd[gp]", 32, BFD_RELOC_NANOMIPS_GPREL19_S2}, + {"ld[gp]", 64, BFD_RELOC_NANOMIPS_GPREL18_S3}, + {"sd[gp]", 64, BFD_RELOC_NANOMIPS_GPREL18_S3}, +}; + +static bfd_reloc_code_real_type +gprel_for_insn (const struct nanomips_opcode *insn) +{ + unsigned int i; + + if (forced_insn_length == 2 + || (forced_insn_format && insn_length (insn) == 2)) + return BFD_RELOC_NANOMIPS_GPREL7_S2; + if (insn_length (insn) == 6) + return BFD_RELOC_NANOMIPS_GPREL_I32; + + for (i = 0; i < ARRAY_SIZE (nanomips_gprel_map); i++) + if (strncasecmp (insn->name, nanomips_gprel_map[i].str, + strlen (insn->name)) == 0 + && strcmp (insn->suffix, + nanomips_gprel_map[i].str + strlen (insn->name)) == 0) + { + if (nanomips_gprel_map[i].gpwidth + && nanomips_gprel_map[i].gpwidth != GPR_SIZE) + continue; + + return nanomips_gprel_map[i].reloc; + } + + return BFD_RELOC_UNUSED; +} + +static bfd_reloc_code_real_type +tlsrel_for_insn (const struct nanomips_opcode *insn, + bfd_reloc_code_real_type rtype) +{ + unsigned int i; + static const struct + { + bfd_reloc_code_real_type key; + bfd_reloc_code_real_type value; + } reloc_map[] = { + { BFD_RELOC_NANOMIPS_TLS_GD, BFD_RELOC_NANOMIPS_TLS_GD_I32 }, + { BFD_RELOC_NANOMIPS_TLS_LD, BFD_RELOC_NANOMIPS_TLS_LD_I32 }, + { BFD_RELOC_NANOMIPS_TLS_TPREL12, BFD_RELOC_NANOMIPS_TLS_TPREL_I32 }, + { BFD_RELOC_NANOMIPS_TLS_DTPREL12, BFD_RELOC_NANOMIPS_TLS_DTPREL_I32 } + }; + + if (insn_length (insn) == 6) + for (i = 0; i < ARRAY_SIZE (reloc_map); i++) + if (rtype == reloc_map[i].key) + return reloc_map[i].value; + + if (strncmp (insn->name, "addiu", 5) == 0 + || strncmp (insn->name, "li", 2) == 0) + { + if (rtype == BFD_RELOC_NANOMIPS_TLS_TPREL12) + return BFD_RELOC_NANOMIPS_TLS_TPREL16; + if (rtype == BFD_RELOC_NANOMIPS_TLS_DTPREL12) + return BFD_RELOC_NANOMIPS_TLS_DTPREL16; + } + return rtype; +} + +static bool +match_insn (struct nanomips_cl_insn *insn, + const struct nanomips_opcode *opcode, + struct nanomips_operand_token *tokens, + unsigned int opcode_extra, bool lax_match) +{ + const char *args; + struct nanomips_arg_info arg; + const struct nanomips_operand *operand; + char c; + + imm_expr.X_op = O_absent; + offset_expr.X_op = O_absent; + offset_reloc[0] = BFD_RELOC_UNUSED; + offset_reloc[1] = BFD_RELOC_UNUSED; + offset_reloc[2] = BFD_RELOC_UNUSED; + + create_insn (insn, opcode); + /* When no opcode suffix is specified, assume ".xyzw". */ + insn->insn_opcode |= opcode_extra; + memset (&arg, 0, sizeof (arg)); + arg.insn = insn; + arg.token = tokens; + arg.argnum = 1; + arg.last_regno = ILLEGAL_REG; + arg.dest_regno = ILLEGAL_REG; + arg.lax_match = lax_match; + arg.select_mask = 0; + + for (args = opcode->args;; ++args) + { + if (arg.token->type == OT_END) + { + /* Handle unary instructions in which only one operand is given. + The source is then the same as the destination. */ + if ((*args == ',') || (arg.opnum == 0 && *args != 0)) + { + operand = decode_nanomips_operand (args + (arg.opnum ? 1 : 0)); + if (operand && nanomips_optional_operand_p (operand)) + { + arg.token = tokens; + arg.argnum = 1; + continue; + } + /* These optional types appear only as the last operand. */ + else if (operand && (operand->type == OP_CP0SEL + || operand->type == OP_DONT_CARE)) + return true; + } + + /* Treat elided base registers as $0. */ + if (strcmp (args, "(b)") == 0 || strcmp (args, "(c)") == 0) + args += 3; + + if ((args[0] == '+' && args[1] == 'N') + || (args[0] == 'm' && args[1] == 'K')) + /* The register suffix is optional. */ + args += 2; + + /* Fail the match if there were too few operands. */ + if (*args) + return false; + + clear_insn_error (); + return true; + } + + /* Fail the match if the line has too many operands. */ + if (*args == 0) + return false; + + /* Handle characters that need to match exactly. */ + if (*args == '(' || *args == ')' || *args == ',') + { + if (match_char (&arg, *args)) + continue; + return false; + } + if (*args == '#') + { + ++args; + if (arg.token->type == OT_DOUBLE_CHAR && arg.token->u.ch == *args) + { + ++arg.token; + continue; + } + return false; + } + + /* Handle special macro operands. Work out the properties of + other operands. */ + arg.opnum += 1; + + switch (*args) + { + case 'I': + if (!match_const_int (&arg, &imm_expr.X_add_number)) + return false; + imm_expr.X_op = O_constant; + if (GPR_SIZE == 32) + normalize_constant_expr (&imm_expr); + continue; + + case 'A': + if (arg.token->type == OT_CHAR && arg.token->u.ch == '(') + { + /* Assume that the offset has been elided and that what + we saw was a base register. The match will fail later + if that assumption turns out to be wrong. */ + offset_expr.X_op = O_constant; + offset_expr.X_add_number = 0; + } + else + { + if (!match_expression (&arg, &offset_expr, offset_reloc)) + return false; + normalize_address_expr (&offset_expr); + } + continue; + + case 'F': + if (!match_float_constant (&arg, &imm_expr, &offset_expr, 8, true)) + return false; + continue; + + case 'L': + if (!match_float_constant (&arg, &imm_expr, &offset_expr, 8, false)) + return false; + continue; + + case 'f': + if (!match_float_constant (&arg, &imm_expr, &offset_expr, 4, true)) + return false; + continue; + + case 'l': + if (!match_float_constant (&arg, &imm_expr, &offset_expr, 4, false)) + return false; + continue; + } + + operand = decode_nanomips_operand (args); + if (!operand) + abort (); + + if (operand->type == OP_PCREL + || operand->type == OP_NON_ZERO_PCREL_S1 + || operand->type == OP_HI20_PCREL + || operand->type == OP_INT_WORD + || operand->type == OP_UINT_WORD + || operand->type == OP_PC_WORD + || operand->type == OP_INT + || operand->type == OP_GPREL_WORD) + switch (*args) + { + case '+': + switch (args[1]) + { + case 'u': + *offset_reloc = BFD_RELOC_NANOMIPS_25_PCREL_S1; + break; + + case 'r': + *offset_reloc = BFD_RELOC_NANOMIPS_21_PCREL_S1; + break; + + case 'Q': + case 'R': + *offset_reloc = BFD_RELOC_NANOMIPS_I32; + break; + } + break; + + case 'a': + *offset_reloc = BFD_RELOC_NANOMIPS_25_PCREL_S1; + break; + + case 'p': + *offset_reloc = BFD_RELOC_NANOMIPS_14_PCREL_S1; + break; + + case '~': + *offset_reloc = BFD_RELOC_NANOMIPS_11_PCREL_S1; + break; + + case 'm': + c = args[1]; + switch (c) + { + case 'D': + *offset_reloc = (forced_insn_length + ? BFD_RELOC_NANOMIPS_10_PCREL_S1 + : BFD_RELOC_UNUSED + RT_BRANCH_UCND); + break; + case 'E': + *offset_reloc = (forced_insn_length + ? BFD_RELOC_NANOMIPS_7_PCREL_S1 + : BFD_RELOC_UNUSED + RT_BRANCH_CNDZ); + break; + case 'F': + *offset_reloc = (forced_insn_length + ? BFD_RELOC_NANOMIPS_4_PCREL_S1 + : BFD_RELOC_UNUSED + RT_BRANCH_CND); + break; + } + break; + } + + /* Skip prefixes. */ + if (*args == '+' || *args == 'm' || *args == '-' || *args == '`') + args++; + + if (nanomips_optional_operand_p (operand) + && args[1] == ',' + && (arg.token[0].type != OT_REG || arg.token[1].type == OT_END)) + { + /* Assume that the register has been elided and is the + same as the first operand. */ + arg.token = tokens; + arg.argnum = 1; + } + + if (operand->type == OP_COPY_BITS) + args++; + + if (!match_operand (&arg, operand)) + return false; + + if (*offset_reloc == BFD_RELOC_NANOMIPS_GPREL18) + *offset_reloc = gprel_for_insn (insn->insn_mo); + else if (flex_reloc_p (*offset_reloc)) + *offset_reloc = tlsrel_for_insn (insn->insn_mo, *offset_reloc); + } +} + +/* Record that the current instruction is invalid for the current ISA. */ + +static void +match_invalid_for_isa (void) +{ + set_insn_error_ss + (0, _("opcode not supported on this processor: %s (%s)"), + nanomips_cpu_info_from_arch (nanomips_opts.arch)->name, + nanomips_cpu_info_from_isa (nanomips_opts.isa, false)); +} + +static bool +match_insns (struct nanomips_cl_insn *insn, + const struct nanomips_opcode *first, + const struct nanomips_opcode *past, + struct nanomips_operand_token *tokens, + int opcode_extra, bool lax_match) +{ + const struct nanomips_opcode *opcode; + bool seen_valid_for_isa, seen_valid_for_size; + bool seen_valid_for_ase, seen_valid_for_fp; + + seen_valid_for_isa = false; + seen_valid_for_size = false; + seen_valid_for_ase = false; + seen_valid_for_fp = false; + opcode = first; + + /* Search for a match, ignoring alternatives that don't satisfy the + current ISA or forced_length. */ + do + { + gas_assert (strcmp (opcode->name, first->name) == 0); + if (!forced_insn_format || strcmp (opcode->suffix, first->suffix) == 0) + { + if (is_opcode_valid (opcode)) + { + seen_valid_for_isa = true; + if (is_size_valid (opcode)) + { + seen_valid_for_size = true; + if (match_insn (insn, opcode, tokens, opcode_extra, + lax_match)) + return true; + } + } + else + { + /* If instruction is not valid for current ISA, try to guess + why for better error reporting. */ + if (is_opcode_valid_def_ase (opcode)) + seen_valid_for_ase = true; + if (is_opcode_valid_for_fp (opcode)) + seen_valid_for_fp = true; + } + } + ++opcode; + } + while (opcode < past && strcmp (opcode->name, first->name) == 0); + + /* Handle the case where we didn't try to match an instruction because + all the alternatives were incompatible with the current ISA. */ + if (!seen_valid_for_isa) + { + match_invalid_for_isa (); + return true; + } + + /* Handle the case where we didn't try to match an instruction because + all the alternatives were of the wrong size. */ + if (!seen_valid_for_size) + { + if (nanomips_opts.insn32) + set_insn_error (0, _("opcode not supported in the `insn32' mode")); + else + set_insn_error_i + (0, _("unrecognized %d-bit version of nanoMIPS opcode"), + 8 * forced_insn_length); + return true; + } + return false; +} + +/* Set up global variables for the start of a new macro. */ + +static void +macro_start (void) +{ + memset (&nanomips_macro_warning.sizes, 0, + sizeof (nanomips_macro_warning.sizes)); + memset (&nanomips_macro_warning.first_insn_sizes, 0, + sizeof (nanomips_macro_warning.first_insn_sizes)); + memset (&nanomips_macro_warning.insns, 0, + sizeof (nanomips_macro_warning.insns)); + nanomips_macro_warning.first_frag = NULL; +} + +/* Finish up a macro. Emit warnings as appropriate. */ + +static void +macro_end (bool allow_expansion) +{ + /* Relaxation warning flags. */ + relax_substateT subtype = 0; + + /* Check instruction count requirements. */ + if (nanomips_macro_warning.insns[0] > 1 + || nanomips_macro_warning.insns[1] > 1) + { + if (nanomips_macro_warning.insns[1] > nanomips_macro_warning.insns[0]) + subtype |= RELAX_SECOND_LONGER; + if (nanomips_opts.nomacro) + subtype |= RELAX_NOMACRO; + } + + /* If both implementations are longer than 1 instruction, then emit the + warning now. */ + if (nanomips_macro_warning.insns[0] > 1 + && nanomips_macro_warning.insns[1] > 1) + { + if (nanomips_opts.nomacro) + { + if (allow_expansion) + as_warn (_("macro instruction expanded into multiple " + "instructions")); + else + as_bad (_("macro instruction expanded into multiple " + "instructions")); + } + + subtype &= ~RELAX_NOMACRO; + } + + /* If any flags still set, then one implementation might need a warning + and the other either will need one of a different kind or none at all. + Pass any remaining flags over to relaxation. */ + if (nanomips_macro_warning.first_frag != NULL) + nanomips_macro_warning.first_frag->fr_subtype |= subtype; +} + +static const char *const brk_fmt[2] = { "mK", "+J" }; +static const char *const addiu_fmt[2] = { "+t,r,j", "t,r,i" }; +static const char *const mfhl_fmt[2] = { "mj", "s" }; + +#define BRK_FMT (brk_fmt[nanomips_opts.insn32]) +#define COP12_FMT "E,+j(b)" +#define JALR_FMT "t,s" +#define LUI_FMT "t,u" +#define DLUI_FMT "mp,+Q" + +#define ADDRESS_ADD_FMT "d,v,t" +#define ADDRESS_ADDI32_FMT "mp,mt,+R" + +#define ADDIU_FMT (addiu_fmt[HAVE_32BIT_ADDRESSES? 0 : 1]) +#define ADDIUGP_FMT "t,ma,." +#define LWGP_FMT "t,.(ma)" +#define LDGP_FMT "t,.(ma)" +#define LL_SC_FMT "t,+p(b)" +#define LLD_SCD_FMT "t,+q(b)" +#define LLE_SCE_FMT "t,+p(b)" +#define LWU_FMT "t,o(b)" +#define MFHL_FMT (mfhl_fmt[nanomips_opts.insn32]) +#define SHFT_FMT "t,r,<" +#define TRAP_FMT "s,t,|" +#define ACLR_FMT "\\,+j(b)" +#define BCONDZ1_FMT "s,p" +#define BCONDZ2_FMT "t,p" +#define B_FMT "+u" +#define JAL_FMT "+u" +#define OP_IMM_FMT "t,r,i" +#define BITOP_IMM_FMT "t,r,g" +#define PREF_FMT "k,+j(b)" +#define BITW_FMT "d,v,t" +#define DIV_FMT "d,v,t" +#define MOVE_FMT "mb,mj" +#define ISA_OFFBITS 12 +#define ISA_ADD_OFFBITS (HAVE_32BIT_ADDRESSES ? 16 : 12) +#define ISA_SIGNED_OFFBITS 9 +#define ISA_MULTI_OFFBITS 9 +#define ISA_COP2_OFFBITS 9 +#define ISA_LLSC_OFFBITS 9 + +#define ISA_LLDSCD_OFFBITS 6 + +#define ISA_CACHE_OFFBITS ISA_LLSC_OFFBITS + +#define MAX_PIC_OFFSET 0x1fffc + +#define MIN_PIC_OFFSET 0 + +#define ISA_UNSIGNED_LDST_RELOC BFD_RELOC_NANOMIPS_LO12 +#define ISA_SIGNED_LDST_RELOC BFD_RELOC_NANOMIPS_SIGNED_9 + +#define ISA_SIGNED_LDST_FMT "t,+j(b)" +#define ISA_MULTI_LDST_FMT "t,+j(b),|" +#define ISA_UNSIGNED_LDST_FMT "t,o(b)" +#define ISA_UNSIGNED_COP1_FMT "T,o(b)" +#define ISA_SIGNED_COP1_FMT "T,+j(b)" + +/* Read a macro's relocation codes from *ARGS and store them in *R. + The first argument in *ARGS will be either the code for a single + relocation or -1 followed by the three codes that make up a + composite relocation. */ + +static void +macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r) +{ + int i, next; + + next = va_arg (*args, int); + if (next >= 0) + r[0] = next; + else + { + for (i = 0; i < 3; i++) + r[i] = va_arg (*args, int); + /* This function is only used for 16-bit relocation fields. + To make the macro code simpler, treat an unrelocated value + in the same way as BFD_RELOC_LO16. */ + } + if (r[0] == BFD_RELOC_UNUSED) + r[0] = BFD_RELOC_LO16; +} + +/* Map an <format,reloc> pair to nanoMIPS relocs. */ + +static void +macro_match_reloc (const char *fmt, bfd_reloc_code_real_type *r) +{ + if (*r == BFD_RELOC_LO16) + { + if (*fmt == 'o' || *fmt == 'g') + *r = BFD_RELOC_NANOMIPS_LO12; + else if (*fmt == 'j') + *r = BFD_RELOC_NANOMIPS_IMM16; + else if (*fmt == 'h') + *r = BFD_RELOC_NANOMIPS_NEG12; + } + else if (*r == BFD_RELOC_GPREL16) + { + if (*fmt == '.') + *r = BFD_RELOC_NANOMIPS_GPREL19_S2; + else if (*fmt == '+' && *(fmt + 1) == '2') + *r = BFD_RELOC_NANOMIPS_GPREL16_S2; + else if (*fmt == '+' && *(fmt + 1) == '1') + *r = BFD_RELOC_NANOMIPS_GPREL18; + else if (*fmt == '+' && *(fmt + 1) == '3') + *r = BFD_RELOC_NANOMIPS_GPREL17_S1; + else if (*fmt == '+' && *(fmt + 1) == '4') + *r = BFD_RELOC_NANOMIPS_GPREL18_S3; + else + gas_assert (false); + } + if (*r == _dummy_first_bfd_reloc_code_real) + *r = BFD_RELOC_UNUSED; + else if (!nanomips_reloc_p (*r)) + { + *r = nanomips_map_reloc (*r); + gas_assert (nanomips_reloc_p (*r)); + } +} + +/* Build an instruction created by a macro expansion. This is passed + a pointer to the count of instructions created so far, an + expression, the name of the instruction to build, an operand format + string, and corresponding arguments. */ + +static void +macro_build (expressionS *ep, const char *name, const char *fmt, ...) +{ + const struct nanomips_opcode *mo = NULL; + bfd_reloc_code_real_type r[3]; + const struct nanomips_opcode *amo; + const struct nanomips_operand *operand; + htab_t hash; + struct nanomips_cl_insn insn; + va_list args; + unsigned int uval; + + va_start (args, fmt); + + r[0] = BFD_RELOC_UNUSED; + r[1] = BFD_RELOC_UNUSED; + r[2] = BFD_RELOC_UNUSED; + hash = nanomips_op_hash; + + amo = str_hash_find (hash, name); + gas_assert (amo); + gas_assert (strcmp (name, amo->name) == 0); + + do + { + /* Search until we get a match for NAME. */ + if (strcmp (fmt, amo->args) == 0 + && amo->pinfo != INSN_MACRO + && is_opcode_valid (amo) + && is_size_valid (amo)) + { + if (!mo) + mo = amo; + } + + ++amo; + gas_assert (amo->name); + } + while (strcmp (name, amo->name) == 0); + + gas_assert (mo); + create_insn (&insn, mo); + for (; *fmt; ++fmt) + { + switch (*fmt) + { + case ',': + case '(': + case ')': + case 'z': + continue; + + case 'i': + case 'g': + case 'j': + case 'h': + macro_read_relocs (&args, r); + macro_match_reloc (fmt, r); + continue; + + case '~': + case 'o': + macro_read_relocs (&args, r); + macro_match_reloc (fmt, r); + continue; + + case 'u': + macro_read_relocs (&args, r); + gas_assert (ep != NULL + && (ep->X_op == O_constant + || (ep->X_op == O_symbol && hi_reloc_p (*r)))); + macro_match_reloc (fmt, r); + continue; + + case '+': + if ((*(fmt + 1) == 'm' + || *(fmt + 1) == 'j' + || *(fmt + 1) == '1' + || *(fmt + 1) == '2' + || *(fmt + 1) == '3' + || *(fmt + 1) == '4' + || *(fmt + 1) == 'K' + || *(fmt + 1) == 'Q' + || *(fmt + 1) == 'R' + || *(fmt + 1) == 'S') + && ep != NULL) + { + macro_read_relocs (&args, r); + macro_match_reloc (fmt, r); + if (*(fmt + 1) == 'Q' || *(fmt + 1) == 'R' || *(fmt + 1) == 'S') + break; + fmt++; + continue; + } + + if (*(fmt + 1) == 'r' || *(fmt + 1) == 'u') + { + if (*(fmt + 1) == 'u') + r[0] = BFD_RELOC_NANOMIPS_25_PCREL_S1; + else if (*(fmt + 1) == 'r') + r[0] = BFD_RELOC_NANOMIPS_21_PCREL_S1; + fmt++; + continue; + } + break; + + case 'p': + gas_assert (ep != NULL); + + /* + * This allows macro() to pass an immediate expression for + * creating short branches without creating a symbol. + * + * We don't allow branch relaxation for these branches, as + * they should only appear in ".set nomacro" anyway. + */ + *r = BFD_RELOC_NANOMIPS_14_PCREL_S1; + macro_match_reloc (fmt, r); + continue; + + case 'm': + if (*(fmt + 1) == 'Q') + { + macro_read_relocs (&args, r); + macro_match_reloc (fmt, r); + fmt++; + continue; + } + break; + + case '.': + macro_read_relocs (&args, r); + macro_match_reloc (fmt, r); + continue; + + case '-': + if (*(fmt + 1) == 'i') + macro_read_relocs (&args, r); + /* Fall through. */ + + default: + break; + } + + operand = decode_nanomips_operand (fmt); + if (!operand) + abort (); + + uval = va_arg (args, int); + + if (operand->type == OP_INT_WORD + || operand->type == OP_UINT_WORD + || operand->type == OP_PC_WORD + || operand->type == OP_GPREL_WORD + || operand->type == OP_IMM_WORD) + insn.insn_opcode_ext = 0; + else if (operand->type == OP_NEG_INT) + insn_insert_operand (&insn, operand, -uval); + else + insn_insert_operand (&insn, operand, uval); + + if ((*fmt == '+') || *fmt == 'm' || *fmt == '-' || *fmt == '`') + ++fmt; + } + + va_end (args); + gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); + append_insn (&insn, ep, r, true); +} + +/* + * Generate an lui/dlui instruction. + */ +static void +macro_build_lui (expressionS *ep, int regnum) +{ + if (ep->X_op != O_constant) + gas_assert (ep->X_op == O_symbol); + + macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_NANOMIPS_HI20); +} + +/* Return the high part that should be loaded in order to make the low + part of VALUE accessible using an offset of OFFBITS bits. */ + +static offsetT +offset_high_part (offsetT value, unsigned int offbits) +{ + offsetT bias; + addressT low_mask; + + if (offbits == 0) + return value; + bias = 1 << (offbits - 1); + low_mask = bias * 2 - 1; + if (offbits == ISA_OFFBITS) + /* Low part is 12-bit unsigned, so no bias necessary. */ + return value & ~low_mask; + else + return (value + bias) & ~low_mask; +} + +/* Return the high part that should be loaded in order to make the low + part of VALUE accessible using an offset of OFFBITS bits. */ + +static offsetT +offset_high_unsigned (offsetT value, unsigned int offbits) +{ + addressT low_mask; + + if (offbits == 0) + return value; + low_mask = (1 << (offbits - 1)) * 2 - 1; + return value & ~low_mask; +} + +/* set_at() + * Generates code to set the $at register to true (one) + * if reg is less than the immediate expression. + */ +static void +set_at (int reg, int unsignedp) +{ + if (offset_high_part (imm_expr.X_add_number, ISA_OFFBITS) == 0) + macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", OP_IMM_FMT, + AT, reg, BFD_RELOC_LO16); + else + { + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT); + } +} + +/* Count the leading zeroes by performing a binary chop. This is a + bulky bit of source, but performance is a LOT better for the + majority of values than a simple loop to count the bits: + for (lcnt = 0; (lcnt < 32); lcnt++) + if ((v) & (1 << (31 - lcnt))) + break; + However it is not code size friendly, and the gain will drop a bit + on certain cached systems. +*/ +#define COUNT_TOP_ZEROES(v) \ + (((v) & ~0xffff) == 0 \ + ? ((v) & ~0xff) == 0 \ + ? ((v) & ~0xf) == 0 \ + ? ((v) & ~0x3) == 0 \ + ? ((v) & ~0x1) == 0 \ + ? !(v) \ + ? 32 \ + : 31 \ + : 30 \ + : ((v) & ~0x7) == 0 \ + ? 29 \ + : 28 \ + : ((v) & ~0x3f) == 0 \ + ? ((v) & ~0x1f) == 0 \ + ? 27 \ + : 26 \ + : ((v) & ~0x7f) == 0 \ + ? 25 \ + : 24 \ + : ((v) & ~0xfff) == 0 \ + ? ((v) & ~0x3ff) == 0 \ + ? ((v) & ~0x1ff) == 0 \ + ? 23 \ + : 22 \ + : ((v) & ~0x7ff) == 0 \ + ? 21 \ + : 20 \ + : ((v) & ~0x3fff) == 0 \ + ? ((v) & ~0x1fff) == 0 \ + ? 19 \ + : 18 \ + : ((v) & ~0x7fff) == 0 \ + ? 17 \ + : 16 \ + : ((v) & ~0xffffff) == 0 \ + ? ((v) & ~0xfffff) == 0 \ + ? ((v) & ~0x3ffff) == 0 \ + ? ((v) & ~0x1ffff) == 0 \ + ? 15 \ + : 14 \ + : ((v) & ~0x7ffff) == 0 \ + ? 13 \ + : 12 \ + : ((v) & ~0x3fffff) == 0 \ + ? ((v) & ~0x1fffff) == 0 \ + ? 11 \ + : 10 \ + : ((v) & ~0x7fffff) == 0 \ + ? 9 \ + : 8 \ + : ((v) & ~0xfffffff) == 0 \ + ? ((v) & ~0x3ffffff) == 0 \ + ? ((v) & ~0x1ffffff) == 0 \ + ? 7 \ + : 6 \ + : ((v) & ~0x7ffffff) == 0 \ + ? 5 \ + : 4 \ + : ((v) & ~0x3fffffff) == 0 \ + ? ((v) & ~0x1fffffff) == 0 \ + ? 3 \ + : 2 \ + : ((v) & ~0x7fffffff) == 0 \ + ? 1 \ + : 0) + +/* + * This routine generates the least number of instructions necessary to load + * an absolute expression value into a register. + */ +static void +load_register (int reg, expressionS *ep, int dbl) +{ + if (ep->X_op != O_big) + { + gas_assert (ep->X_op == O_constant); + + /* Sign-extending 32-bit constants makes their handling easier. */ + if (!dbl) + normalize_constant_expr (ep); + + if ((int)ep->X_add_number >= -1 + && ep->X_add_number <= 126 + && (reg >> 2 == 1 || reg >> 2 == 4)) + { + /* 7-bit values loaded using LI[16]. */ + macro_build (NULL, "li", "md,mI", reg, (int) ep->X_add_number); + return; + } + if (IS_SEXT_16BIT_UINT (ep->X_add_number)) + { + /* We can handle 16 bit unsigned values with an addiu to + $zero. No need to ever use daddiu here, since $zero and + the result are always correct in 32 bit mode. */ + macro_build (ep, ADDRESS_ADDI_INSN, ADDIU_FMT, reg, 0, + BFD_RELOC_NANOMIPS_IMM16); + return; + } + if (offset_high_unsigned (-ep->X_add_number, ISA_OFFBITS) == 0) + { + macro_build (ep, "addiu", "t,r,h", reg, 0, BFD_RELOC_NANOMIPS_NEG12); + return; + } + else if ((IS_SEXT_32BIT_NUM (ep->X_add_number))) + { + if ((nanomips_opts.ase & ASE_xNMS) != 0 + && (ep->X_add_number & 0xfff) != 0 + && (*offset_reloc == BFD_RELOC_UNUSED + || pcrel_reloc_p (*offset_reloc)) + && !nanomips_opts.insn32) + macro_build (ep, "li", "mp,+Q", reg, BFD_RELOC_NANOMIPS_I32); + else + { + /* 32 bit values require an lui. */ + macro_build (ep, "lui", "t,u", reg, BFD_RELOC_NANOMIPS_HI20); + if ((ep->X_add_number & 0xfff) != 0 + && !hi_reloc_p (*offset_reloc)) + macro_build (ep, "ori", BITOP_IMM_FMT, reg, reg, + BFD_RELOC_NANOMIPS_LO12); + } + return; + } + } + + return; +} + +/* Move the contents of register SOURCE into register DEST. */ + +static void +move_register (int dest, int source) +{ + /* Prefer to use a 16-bit instruction unless a 32-bit one + is specifically requested. */ + if (!nanomips_opts.insn32) + macro_build (NULL, "move", MOVE_FMT, dest, source); + else + macro_build (NULL, "or", "d,v,t", dest, source, 0); +} + +/* Emit a two-argument branch macro specified by TYPE, using SREG as + the register tested. EP specifies the branch target. */ + +static void +macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg) +{ + const char *br; + + switch (type) + { + case M_BGEZ: + br = "bgez"; + break; + case M_BGTZ: + br = "bgtz"; + break; + case M_BLEZ: + br = "blez"; + break; + case M_BLTZ: + br = "bltz"; + break; + default: + abort (); + } + + if (type == M_BGTZ || type == M_BLEZ) + macro_build (ep, br, BCONDZ2_FMT, sreg); + else + macro_build (ep, br, BCONDZ1_FMT, sreg); +} + +/* Emit a three-argument branch macro specified by TYPE, using SREG and + TREG as the registers tested. EP specifies the branch target. */ + +static void +macro_build_branch_rsrt (int type, expressionS *ep, + unsigned int sreg, unsigned int treg) +{ + const char *br; + + switch (type) + { + case M_BEQ: + case M_BEQ_I: + br = "beq"; + break; + case M_BNE: + case M_BNE_I: + br = "bne"; + break; + default: + abort (); + } + + if (treg == 0) + macro_build (ep, br, "s,t,p", treg, sreg); + else + macro_build (ep, br, "s,t,p", sreg, treg); +} + +/* Emit a three-argument branch macro specified by TYPE, using SREG and + TREG as the registers tested. EP specifies the branch target. */ + +static void +macro_build_branch_rtim (int type, expressionS *ep, + unsigned int sreg, expressionS *imex) +{ + const char *br; + int imm = imex->X_add_number; + + switch (type) + { + case M_BEQ_I: + br = "beqic"; + break; + case M_BGE_I: + br = "bgeic"; + break; + case M_BGEU_I: + case M_BGTU_I: + br = "bgeiuc"; + break; + case M_BLT_I: + case M_BLE_I: + br = "bltic"; + break; + case M_BLTU_I: + case M_BLEU_I: + br = "bltiuc"; + break; + case M_BNE_I: + br = "bneic"; + break; + default: + abort (); + } + + macro_build (ep, br, "t,+9,~", sreg, imm, BFD_RELOC_NANOMIPS_11_PCREL_S1); +} + +/* Return true if the value stored in offset_expr and offset_reloc + fits into a signed offset of OFFBITS bits. RANGE is the maximum + amount that the caller wants to add without inducing overflow + and ALIGN is the known alignment of the value in bytes. */ + +static bool +small_offset_p (unsigned int range, unsigned int align, unsigned int offbits) +{ + if (offbits == 16) + { + /* Accept any relocation operator if overflow isn't a concern. */ + if (range < align && *offset_reloc != BFD_RELOC_UNUSED) + return true; + + /* These relocations are guaranteed not to overflow in correct links. */ + if (gprel_reloc_p (*offset_reloc)) + return true; + } + + if (offset_expr.X_op == O_constant + && offset_high_part (offset_expr.X_add_number, offbits) == 0 + && offset_high_part (offset_expr.X_add_number + range, offbits) == 0 + && offset_expr.X_add_number % align == 0 + && range % align == 0) + return true; + + return false; +} + +static bool +small_poffset_p (unsigned int range, unsigned align, unsigned int offbits) +{ + if (offset_expr.X_op == O_constant + && offset_expr.X_add_number >= 0 + && offset_expr.X_add_number + range < (1 << offbits) + && offset_expr.X_add_number % align == 0 + && range % align == 0) + return true; + + if (lo_reloc_p (*offset_reloc)) + return true; + + return false; +} + +static bool +small_noffset_p (unsigned int range, unsigned int offbits) +{ + if (offset_expr.X_op == O_constant + && offset_expr.X_add_number < 0 + && -offset_expr.X_add_number + range < (1 << offbits)) + return true; + + return false; +} + +/* Check if RANGE fits in positive/negative add-immediate limits. */ + +static bool +small_add_offset_p (unsigned int range) +{ + return (small_poffset_p (range, 1, ISA_ADD_OFFBITS) + || (small_noffset_p (range, ISA_OFFBITS))); +} + +/* Check whether EXP is a valid expression for macro expansions + to handle in the current arch mode. */ + +static bool +macro_expression_valid (expressionS *exp, bfd_reloc_code_real_type rtype) +{ + if ((exp->X_op != O_constant && exp->X_op != O_symbol) + || (exp->X_op == O_symbol && hi_reloc_p (rtype))) + { + as_bad (_("expression too complex")); + exp->X_op = O_constant; + return false; + } + + if (HAVE_64BIT_ADDRESSES && nanomips_opts.insn32) + { + as_bad (_("Macro expansions for 64-bit addresses not supported" + "in insn32 mode")); + return false; + } + + if (exp->X_op == O_constant && hi_reloc_p (rtype)) + exp->X_add_number = offset_high_part (exp->X_add_number, ISA_OFFBITS); +} + +/* Expand the LA macro for PC-relative addressing. */ + +static void +macro_pcrel_la (unsigned int dest, expressionS *an_expr, + bfd_reloc_code_real_type r) +{ + if ((nanomips_opts.ase & ASE_xNMS) != 0 + && r == BFD_RELOC_UNUSED + && !nanomips_opts.insn32) + macro_build (an_expr, "lapc", "mp,+S", dest, + BFD_RELOC_NANOMIPS_PC_I32); + else + { + macro_build (an_expr, "aluipc", "t,+K", dest, + BFD_RELOC_NANOMIPS_PCREL_HI20); + macro_build (an_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, + dest, dest, BFD_RELOC_NANOMIPS_LO12); + } +} + +/* Expand the LA macro for absolute addressing. */ + +static void +macro_absolute_la (unsigned int dest) +{ + if ((nanomips_opts.ase & ASE_xNMS) != 0 + && *offset_reloc == BFD_RELOC_UNUSED + && !nanomips_opts.insn32 + && !HAVE_64BIT_SYMBOLS) + macro_build (&offset_expr, "li", "mp,+Q", dest, BFD_RELOC_NANOMIPS_I32); + else + { + macro_build_lui (&offset_expr, dest); + if (!hi_reloc_p (*offset_reloc)) + macro_build (&offset_expr, "ori", BITOP_IMM_FMT, dest, dest, + ISA_UNSIGNED_LDST_RELOC); + } +} + +/* Expand the LA macro. */ + +static void +macro_la (unsigned int op[], unsigned int breg, int *used_at, + bool gprel_ok) +{ + unsigned int tempreg; + + if (small_poffset_p (0, 1, ISA_ADD_OFFBITS)) + { + macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, op[0], breg, + -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]); + return; + } + else if (small_noffset_p (0, ISA_OFFBITS)) + { + macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h", op[0], breg, + -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]); + return; + } + + if (!nanomips_opts.noat && (op[0] == breg)) + { + tempreg = AT; + *used_at = 1; + } + else + tempreg = op[0]; + + if (!macro_expression_valid (&offset_expr, offset_reloc[0])) + return; + + if (offset_expr.X_op == O_constant) + load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES); + else if (nanomips_opts.pic == NO_PIC) + { + if (gprel_ok + && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET + && !nopic_need_relax (offset_expr.X_add_symbol, 1) + && (nanomips_opts.mc_model == MC_MEDIUM + || nanomips_opts.mc_model == MC_AUTO)) + { + relax_start (offset_expr.X_add_symbol); + macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIUGP_FMT, + op[0], nanomips_gp_register, BFD_RELOC_GPREL16); + relax_switch (); + } + + if (nanomips_opts.pcrel) + macro_pcrel_la (op[0], &offset_expr, *offset_reloc); + else + macro_absolute_la (op[0]); + + if (nanomips_relax.sequence) + relax_end (); + } + else + { + relax_start (offset_expr.X_add_symbol); + + if (nanomips_opts.pic == SVR4_PIC || linkrelax) + macro_build (&offset_expr, ADDRESS_LOAD_INSN, LWGP_FMT, tempreg, + BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register); + else if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32) + macro_build (&offset_expr, PCREL_LOAD_INSN, "mp,+S", tempreg, + BFD_RELOC_NANOMIPS_GOTPC_I32); + else + { + macro_build (&offset_expr, "aluipc", "t,+K", op[0], + BFD_RELOC_NANOMIPS_GOTPC_HI20); + macro_build (&offset_expr, ADDRESS_LOAD_INSN, ISA_UNSIGNED_LDST_FMT, + tempreg, BFD_RELOC_NANOMIPS_GOT_LO12, op[0]); + } + + relax_switch (); + /* Local symbols can be PC-relative. */ + macro_pcrel_la (op[0], &offset_expr, *offset_reloc); + relax_end (); + } + + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg); +} + +static void +macro_absolute_ld_st (const char *s, const char *fmt, unsigned int op[], + unsigned int tempreg, unsigned int breg) +{ + macro_build_lui (&offset_expr, tempreg); + + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); + + macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg); +} + +static void +macro_pcrel_ld_st (const char *s, const char *fmt, unsigned int op[], + unsigned int tempreg, bool coproc) +{ + if ((nanomips_opts.ase & ASE_xNMS) != 0 + && *offset_reloc == BFD_RELOC_UNUSED + && !nanomips_opts.insn32 + && !coproc + && (s[0] == 's' || s[0] == 'l') + && (s[1] == 'w' || s[1] == 'd') + && s[2] == 0) + { + char insn[] = "xxpc"; + strncpy (insn, s, 2); + macro_build (&offset_expr, insn, "mp,+S", op[0], + BFD_RELOC_NANOMIPS_PC_I32); + } + else + { + macro_build (&offset_expr, "aluipc", "t,+K", tempreg, + BFD_RELOC_NANOMIPS_PCREL_HI20); + macro_build (&offset_expr, s, fmt, + op[0], BFD_RELOC_NANOMIPS_LO12, tempreg); + } +} + +static void +macro_ld_st (const char *s, const char *fmt, unsigned int op[], + const char *gpfmt, int align, int offbits, unsigned int breg, + unsigned int tempreg, int *used_at, bool coproc) +{ + expressionS expr1; + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + + if (small_add_offset_p (0)) + { + /* The first case exists for M_LD_AB and M_SD_AB, which are + macros for o32 but which should act like normal instructions + otherwise. */ + if ((offbits == ISA_OFFBITS || strncmp (s, "ua", 2) == 0) + && small_poffset_p (0, align, offbits)) + macro_build (&offset_expr, s, fmt, op[0], offset_reloc[0], breg); + else if (small_offset_p (0, align, offbits)) + macro_build (NULL, s, fmt, op[0], + (int) offset_expr.X_add_number, breg); + else + { + /* Source destroying loads do not need AT for expansion. */ + if (s[0] == 'l' && op[0] == breg && op[0] != 0) + tempreg = breg; + else if (tempreg == AT) + *used_at = 1; + + if (small_poffset_p (0, 1, ISA_ADD_OFFBITS)) + macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, + tempreg, breg, -1, offset_reloc[0], + offset_reloc[1], offset_reloc[2]); + else if (small_noffset_p (0, ISA_OFFBITS)) + macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h", + tempreg, breg, -1, offset_reloc[0], + offset_reloc[1], offset_reloc[2]); + macro_build (NULL, s, fmt, op[0], 0, tempreg); + } + return; + } + + if (tempreg == AT) + *used_at = 1; + + if (!macro_expression_valid (&offset_expr, offset_reloc[0])) + return; + + /* A constant expression in PIC code can be handled just as it + is in non PIC code. */ + if (offset_expr.X_op == O_constant) + { + if (op[0] == breg + && op[0] != 0 + && (nanomips_opts.ase & ASE_xNMS) != 0 + && (offset_high_part (offset_expr.X_add_number, offbits) + != offset_expr.X_add_number)) + { + *used_at = 0; + macro_build (&offset_expr, "addiu", "mp,mt,+R", op[0], op[0], + BFD_RELOC_NANOMIPS_I32); + macro_build (NULL, s, fmt, op[0], 0, op[0]); + } + else + { + expr1.X_add_number = offset_high_part (offset_expr.X_add_number, + offbits); + if (offbits != ISA_OFFBITS && (expr1.X_add_number & 0xfff) != 0) + expr1 = offset_expr; + offset_expr.X_add_number -= expr1.X_add_number; + + load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES); + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, + tempreg, breg); + + if (offbits == ISA_OFFBITS) + macro_build (&offset_expr, s, fmt, op[0], + BFD_RELOC_NANOMIPS_LO12, tempreg); + else + macro_build (NULL, s, fmt, op[0], (int) offset_expr.X_add_number, + tempreg); + } + } + else if (offbits != ISA_OFFBITS) + { + /* The offset field is too narrow to be used for a low-part + relocation, so load the whole address into the auxillary + register. */ + macro_la (&tempreg, 0, used_at, strncmp (s, "ua", 2) != 0); + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg); + macro_build (NULL, s, fmt, op[0], 0, tempreg); + } + else if (nanomips_opts.pic == NO_PIC) + { + if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET + && !nopic_need_relax (offset_expr.X_add_symbol, 1) + && (nanomips_opts.mc_model == MC_MEDIUM + || nanomips_opts.mc_model == MC_AUTO)) + { + relax_start (offset_expr.X_add_symbol); + if (gpfmt == NULL) + gpfmt = fmt; + macro_build (&offset_expr, s, gpfmt, op[0], + BFD_RELOC_GPREL16, nanomips_gp_register); + relax_switch (); + } + + if (nanomips_opts.pcrel) + macro_pcrel_ld_st (s, fmt, op, tempreg, coproc); + else + macro_absolute_ld_st (s, fmt, op, tempreg, breg); + + if (nanomips_relax.sequence) + relax_end (); + } + else + { + gas_assert (offset_expr.X_op == O_symbol); + + gpfmt = "t,.(ma)"; + relax_start (offset_expr.X_add_symbol); + + if (nanomips_opts.pic == SVR4_PIC || linkrelax) + macro_build (&offset_expr, ADDRESS_LOAD_INSN, gpfmt, tempreg, + BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register); + else if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32) + macro_build (&offset_expr, PCREL_LOAD_INSN, "mp,+S", tempreg, + BFD_RELOC_NANOMIPS_GOTPC_I32); + else + { + macro_build (&offset_expr, "aluipc", "t,+K", tempreg, + BFD_RELOC_NANOMIPS_GOTPC_HI20); + macro_build (&offset_expr, ADDRESS_LOAD_INSN, ISA_UNSIGNED_LDST_FMT, + tempreg, BFD_RELOC_NANOMIPS_GOT_LO12, tempreg); + } + macro_build (NULL, s, fmt, op[0], 0, tempreg); + + relax_switch (); + macro_pcrel_ld_st (s, fmt, op, tempreg, coproc); + relax_end (); + } +} + +/* Generate a pair of loads or stores, either as a multi-word instruction + derived from S or 2 subsequent instructions: S. If MULTI_OK, then + the EP admits no additional relocation, R can be ignored. */ +static void +macro_ld_st_pair (const char *s, const char *fmt, unsigned int op[], + unsigned int breg, expressionS *ep, + bfd_reloc_code_real_type r, bool multi_ok) +{ + if (multi_ok && (nanomips_opts.ase & ASE_xNMS) != 0) + { + char ms[10]; + if (snprintf (ms, sizeof(ms), "%sm", s) >= (int)sizeof(ms)) + gas_assert (false); + macro_build (ep, ms, ISA_MULTI_LDST_FMT, op[0], r, breg, 2); + } + else + { + macro_build (ep, s, fmt, op[0], r, breg); + ep->X_add_number += 4; + macro_build (ep, s, fmt, op[0] + 1, r, breg); + } +} + +static void +macro_absolute_ldd_std (const char *s, const char *fmt,unsigned int op[], + unsigned int breg, unsigned int offbits) +{ + if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32) + { + macro_build (&offset_expr, "li", "mp,+Q", AT, BFD_RELOC_NANOMIPS_I32); + offset_expr.X_op = O_constant; + offset_expr.X_add_number = 0; + } + else + { + macro_build_lui (&offset_expr, AT); + + if (offbits != ISA_OFFBITS + || (offset_high_part (offset_expr.X_add_number, ISA_OFFBITS) + != offset_high_part (offset_expr.X_add_number + 4, ISA_OFFBITS))) + { + macro_build (&offset_expr, "ori", BITOP_IMM_FMT, AT, AT, + BFD_RELOC_NANOMIPS_LO12); + offset_expr.X_op = O_constant; + offset_expr.X_add_number = 0; + } + } + + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); + + macro_ld_st_pair (s, fmt, op, AT, &offset_expr, BFD_RELOC_NANOMIPS_LO12, + offset_expr.X_op == O_constant); +} + +static void +macro_pcrel_ldd_std (const char *s, const char *fmt, + unsigned int op[], unsigned int offbits) +{ + if (offbits != ISA_OFFBITS) + { + expressionS an_expr; + an_expr.X_op = O_constant; + an_expr.X_op_symbol = NULL; + an_expr.X_add_symbol = NULL; + an_expr.X_add_number = 4; + macro_pcrel_la (AT, &offset_expr, *offset_reloc); + + macro_ld_st_pair (s, fmt, op, AT, &an_expr, + BFD_RELOC_NANOMIPS_SIGNED_9, true); + } + else if ((nanomips_opts.ase & ASE_xNMS) != 0 + && *offset_reloc == BFD_RELOC_UNUSED + && !nanomips_opts.insn32) + { + const char *insn = (strcmp (s, "lw") == 0 ? "lwpc" : "swpc"); + macro_build (&offset_expr, insn, "mp,+S", op[0], + BFD_RELOC_NANOMIPS_PC_I32); + offset_expr.X_add_number += 4; + macro_build (&offset_expr, insn, "mp,+S", op[0] + 1, + BFD_RELOC_NANOMIPS_PC_I32); + } + else + { + macro_build (&offset_expr, "aluipc", "t,+K", AT, + BFD_RELOC_NANOMIPS_PCREL_HI20); + macro_ld_st_pair (s, fmt, op, AT, &offset_expr, + BFD_RELOC_NANOMIPS_LO12, false); + } +} + +static void +macro_ldd_std (const char *s, const char *fmt, unsigned int op[], int align, + int offbits, unsigned int breg, int *used_at) +{ + expressionS expr1; + expressionS *ep; + + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + + if (small_add_offset_p (0)) + { + ep = &offset_expr; + if (offbits == ISA_OFFBITS && small_poffset_p (4, align, ISA_OFFBITS)) + offset_reloc[0] = ISA_UNSIGNED_LDST_RELOC; + else if (offbits == ISA_SIGNED_OFFBITS + && small_offset_p (4, align, ISA_SIGNED_OFFBITS)) + offset_reloc[0] = ISA_SIGNED_LDST_RELOC; + else + { + if (small_poffset_p (0, align, ISA_ADD_OFFBITS)) + macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, AT, + breg, offset_reloc[0]); + else if (small_noffset_p (0, ISA_OFFBITS)) + macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h", AT, + breg, offset_reloc[0]); + + expr1.X_add_number = 0; + ep = &expr1; + breg = AT; + *used_at = 1; + offset_reloc[0] = BFD_RELOC_UNUSED; + } + + if (strstr (s, "lw") != NULL && op[0] == breg) + { + ep->X_add_number += 4; + macro_build (ep, s, fmt, op[0] + 1, offset_reloc[0], breg); + ep->X_add_number -= 4; + macro_build (ep, s, fmt, op[0], offset_reloc[0], breg); + } + else + macro_ld_st_pair (s, fmt, op, breg, ep, offset_reloc[0], + (small_offset_p (0, align, ISA_MULTI_OFFBITS) + || expr1.X_add_number == 0)); + return; + } + + if (!macro_expression_valid (&offset_expr, offset_reloc[0])) + return; + + if (nanomips_opts.pic == NO_PIC || offset_expr.X_op == O_constant) + { + if (offset_expr.X_op == O_symbol + && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET + && strncmp (s, "ua", 2) != 0 + && (breg == 0 + && (gprel_reloc_p (*offset_reloc) + || *offset_reloc == BFD_RELOC_UNUSED)) + && !nopic_need_relax (offset_expr.X_add_symbol, 1)) + { + const char *sfmt = fmt; + unsigned int tempreg; + relax_start (offset_expr.X_add_symbol); + if (breg == 0) + { + tempreg = nanomips_gp_register; + sfmt = "t,.(ma)"; + } + else + { + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", + AT, breg, nanomips_gp_register); + tempreg = AT; + *used_at = 1; + } + + macro_build (&offset_expr, s, sfmt, op[0], BFD_RELOC_GPREL16, + tempreg); + offset_expr.X_add_number += 4; + macro_build (&offset_expr, s, sfmt, op[0] + 1, + BFD_RELOC_GPREL16, tempreg); + relax_switch (); + + offset_expr.X_add_number -= 4; + } + + *used_at = 1; + if (nanomips_opts.pcrel) + macro_pcrel_ldd_std (s, fmt, op, offbits); + else + macro_absolute_ldd_std (s, fmt, op, breg, offbits); + + + if (nanomips_relax.sequence) + relax_end (); + } + else if (offbits == ISA_OFFBITS) + { + *used_at = 1; + expr1.X_add_number = 0; + relax_start (offset_expr.X_add_symbol); + macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,.(ma)", AT, + BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register); + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); + + macro_ld_st_pair (s, fmt, op, AT, &expr1, BFD_RELOC_NANOMIPS_LO12, true); + + relax_switch (); + macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,.(ma)", AT, + BFD_RELOC_NANOMIPS_GOT_PAGE, nanomips_gp_register); + if (breg != 0) + macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT); + + macro_build (&offset_expr, s, fmt, op[0], + BFD_RELOC_NANOMIPS_GOT_OFST, AT); + offset_expr.X_add_number += 4; + macro_build (&offset_expr, s, fmt, op[0] + 1, + BFD_RELOC_NANOMIPS_GOT_OFST, AT); + relax_end (); + } + else + { + *used_at = 1; + expr1.X_add_number = 0; + macro_la (&nanomips_opts.noat, 0, used_at, true); + macro_ld_st_pair (s, fmt, op, AT, &expr1, + BFD_RELOC_NANOMIPS_LO12, true); + } +} + +/* Build macros */ + +static void +macro (struct nanomips_cl_insn *ip, char *str ATTRIBUTE_UNUSED) +{ + const struct nanomips_operand_array *operands; + + unsigned int breg, i; + unsigned int tempreg; + int mask; + int used_at = 0; + expressionS label_expr; + expressionS expr1; + const char *s = ip->insn_mo->name; + const char *s2 = NULL; + const char *fmt; + const char *gpfmt = NULL; + int coproc = 0; + int offbits = ISA_OFFBITS; + bool dbl = false; + int lp = 0; + unsigned int align; + unsigned int op[MAX_OPERANDS]; + + operands = insn_operands (ip); + for (i = 0; i < MAX_OPERANDS; i++) + if (operands->operand[i]) + op[i] = insn_extract_operand (ip, operands->operand[i]); + else + op[i] = -1; + + mask = ip->insn_mo->mask; + + label_expr.X_op = O_constant; + label_expr.X_op_symbol = NULL; + label_expr.X_add_symbol = NULL; + label_expr.X_add_number = 0; + + expr1.X_op = O_constant; + expr1.X_op_symbol = NULL; + expr1.X_add_symbol = NULL; + expr1.X_add_number = 1; + align = 1; + + switch (mask) + { + case M_ABS: + /* bgez $a0,1f + move v0,$a0 + sub v0,$zero,$a0 + 1: + */ + + nanomips_label_expr (&label_expr); + if (op[0] != op[1]) + move_register (op[0], op[1]); + macro_build (&label_expr, "bgezc", BCONDZ1_FMT, op[1]); + macro_build (NULL, "subu", "d,v,t", op[0], 0, op[1]); + nanomips_add_label (); + break; + + case M_ADD_I: + s = "addiu"; + s2 = "add"; + if (imm_expr.X_add_number == 0) + goto do_addi; + else + goto do_addi_i; + case M_ADDU_I: + s = "addiu"; + s2 = "addu"; + do_addi: + if (offset_high_unsigned (imm_expr.X_add_number, 16) == 0) + { + macro_build (&imm_expr, s, ADDIU_FMT, op[0], op[1], + BFD_RELOC_NANOMIPS_IMM16); + break; + } + else if (offset_high_unsigned (-imm_expr.X_add_number, 12) == 0) + { + macro_build (&imm_expr, s, "t,r,h", op[0], op[1], + BFD_RELOC_NANOMIPS_NEG12); + break; + } + else if ((nanomips_opts.ase & ASE_xNMS) != 0 + && op[0] == op[1] + && !nanomips_opts.insn32) + { + macro_build (&imm_expr, s, "mp,mt,+R", op[0], op[0], + BFD_RELOC_NANOMIPS_I32); + break; + } + do_addi_i: + used_at = 1; + load_register (AT, &imm_expr, dbl); + macro_build (NULL, s2, "d,v,t", op[0], op[1], AT); + break; + + case M_AND_I: + s = "andi"; + s2 = "and"; + goto do_bit; + case M_OR_I: + s = "ori"; + s2 = "or"; + goto do_bit; + case M_NOR_I: + s = ""; + s2 = "nor"; + goto do_load_op; + case M_XOR_I: + s = "xori"; + s2 = "xor"; + do_bit: + if (offset_high_unsigned (imm_expr.X_add_number, 12) == 0) + { + if (mask != M_NOR_I) + macro_build (&imm_expr, s, BITOP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + else + { + macro_build (&imm_expr, "ori", BITOP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + macro_build (NULL, "nor", "d,v,t", op[0], op[0], 0); + } + break; + } + do_load_op: + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, s2, "d,v,t", op[0], op[1], AT); + break; + + case M_BEQ_I: + case M_BNE_I: + if (imm_expr.X_add_number == 0) + op[1] = 0; + else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0) + { + macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr); + break; + } + else + { + op[1] = AT; + used_at = 1; + load_register (op[1], &imm_expr, GPR_SIZE == 64); + } + macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]); + break; + + case M_BGE: + if (op[1] == 0) + macro_build_branch_rs (M_BGEZ, &offset_expr, op[0]); + else if (op[0] == 0) + macro_build_branch_rs (M_BLEZ, &offset_expr, op[1]); + else + { + used_at = 1; + macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]); + macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO); + } + break; + + case M_BGT: + if (op[1] == 0) + macro_build_branch_rs (M_BGTZ, &offset_expr, op[0]); + else if (op[0] == 0) + macro_build_branch_rs (M_BLTZ, &offset_expr, op[1]); + else + { + used_at = 1; + macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_BLE: + if (op[1] == 0) + macro_build_branch_rs (M_BLEZ, &offset_expr, op[0]); + else if (op[0] == 0) + macro_build_branch_rs (M_BGEZ, &offset_expr, op[1]); + else + { + used_at = 1; + macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]); + macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO); + } + break; + + case M_BLT: + if (op[1] == 0) + macro_build_branch_rs (M_BLTZ, &offset_expr, op[0]); + else if (op[0] == 0) + macro_build_branch_rs (M_BGTZ, &offset_expr, op[1]); + else + { + used_at = 1; + macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_BGEZAL: + s = "bltzc"; + goto brcond_al; + case M_BLTZAL: + s = "bgezc"; + brcond_al: + nanomips_label_expr (&label_expr); + macro_build (&label_expr, s, BCONDZ1_FMT, op[0]); + macro_build (&offset_expr, "bal", B_FMT); + nanomips_add_label (); + break; + + case M_BGT_I: + /* Check for > max integer. */ + if (imm_expr.X_add_number >= GPR_SMAX) + { + do_false: + /* Result is always false. */ + macro_build (NULL, "nop", ""); + break; + } + ++imm_expr.X_add_number; + /* Fall through. */ + case M_BGE_I: + if (imm_expr.X_add_number == 0) + macro_build_branch_rs (M_BGEZ, &offset_expr, op[0]); + else if (imm_expr.X_add_number == 1) + macro_build_branch_rs (M_BGTZ, &offset_expr, op[0]); + else if (imm_expr.X_add_number <= GPR_SMIN) + { + do_true: + /* result is always true */ + as_warn (_("branch %s is always true"), ip->insn_mo->name); + macro_build (&offset_expr, "b", B_FMT); + break; + } + else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0) + macro_build_branch_rtim (M_BGE_I, &offset_expr, op[0], &imm_expr); + else + { + used_at = 1; + set_at (op[0], 0); + macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO); + } + break; + + case M_BGTU_I: + if (op[0] == 0 || (GPR_SIZE == 32 && imm_expr.X_add_number == -1)) + goto do_false; + ++imm_expr.X_add_number; + /* Fall through. */ + case M_BGEU_I: + if (imm_expr.X_add_number == 0) + goto do_true; + else if (imm_expr.X_add_number == 1) + macro_build_branch_rsrt (M_BNE, &offset_expr, op[0], ZERO); + else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0) + macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr); + else + { + used_at = 1; + set_at (op[0], 1); + macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO); + } + break; + + case M_BGTU: + if (op[1] == 0) + macro_build_branch_rsrt (M_BNE, &offset_expr, op[0], ZERO); + else if (op[0] == 0) + goto do_false; + else + { + used_at = 1; + macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_BLE_I: + if (imm_expr.X_add_number >= GPR_SMAX) + goto do_true; + ++imm_expr.X_add_number; + /* Fall through. */ + case M_BLT_I: + if (imm_expr.X_add_number == 0) + macro_build_branch_rs (M_BLTZ, &offset_expr, op[0]); + else if (imm_expr.X_add_number == 1) + macro_build_branch_rs (M_BLEZ, &offset_expr, op[0]); + else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0) + macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr); + else + { + used_at = 1; + set_at (op[0], 0); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_BLEU: + if (op[1] == 0) + macro_build_branch_rsrt (M_BEQ, &offset_expr, op[0], ZERO); + else if (op[0] == 0) + goto do_true; + else + { + used_at = 1; + macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]); + macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO); + } + break; + + case M_BLEU_I: + if (op[0] == 0 || (GPR_SIZE == 32 && imm_expr.X_add_number == -1)) + goto do_true; + ++imm_expr.X_add_number; + /* Fall through. */ + case M_BLTU_I: + if (imm_expr.X_add_number == 0) + goto do_false; + else if (imm_expr.X_add_number == 1) + macro_build_branch_rsrt (M_BEQ, &offset_expr, op[0], ZERO); + else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0) + macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr); + else + { + used_at = 1; + set_at (op[0], 1); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_BLTU: + if (op[1] == 0) + goto do_false; + else if (op[0] == 0) + macro_build_branch_rsrt (M_BNE, &offset_expr, ZERO, op[1]); + else + { + used_at = 1; + macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]); + macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO); + } + break; + + case M_EXT: + if (31 - op[2] - op[3] > 0) + { + macro_build (NULL, "sll", SHFT_FMT, op[0], op[1], + 31 - op[2] - op[3]); + macro_build (NULL, "srl", SHFT_FMT, op[0], op[0], 31 - op[3]); + } + else + macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], op[2]); + break; + + case M_INS: + if (op[0] == op[1] && op[2] == 0) + { + macro_build (NULL, "nop", ""); + break; + } + else if (op[0] == op[1]) + { + used_at = 1; + macro_build (NULL, "extw", "d,s,t,+I", AT, op[0], op[0], op[2]); + macro_build (NULL, "extw", "d,s,t,+I", op[0], AT, op[0], + 1 + op[3] - op[2]); + } + else if (op[2] == 0 && op[3] == 31) + { + move_register (op[0], op[1]); + break; + } + else + { + if (op[2] != 0) + macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0], + op[0], op[2]); + macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0], op[1], + 1 + op[3] - op[2]); + } + if (op[3] < 31) + macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0], op[0], + 31 - op[3]); + break; + + case M_DIV_I: + case M_DIVU_I: + goto do_divi; + + case M_REM_3I: + case M_MOD_I: + s = "mod"; + do_divi: + if (imm_expr.X_add_number == 0) + { + as_warn (_("divide by zero")); + if (nanomips_trap) + macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7); + else + macro_build (NULL, "break", BRK_FMT, 7); + break; + } + if (imm_expr.X_add_number == 1) + { + if (strncmp (s, "div", 3) == 0) + move_register (op[0], op[1]); + else + move_register (op[0], ZERO); + break; + } + if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u') + { + if (strncmp (s, "div", 3) == 0) + macro_build (NULL, "neg", "d,w", op[0], op[1]); + else + move_register (op[0], ZERO); + break; + } + + used_at = 1; + load_register (AT, &imm_expr, dbl); + macro_build (NULL, s, DIV_FMT, op[0], op[1], AT); + break; + + case M_MODU_I: + s = "modu"; + goto do_divi; + + case M_LA_AB: + /* Load the address of a symbol into a register. If breg is not + zero, we then add a base register to it. */ + breg = op[2]; + macro_la (op, breg, &used_at, true); + break; + + case M_J_A: + /* The j instruction may not be used in PIC code, since it + requires an absolute address. We convert it to a b + instruction. */ + /* Likewise for nanoMIPS which has no absolute jump instruction. */ + macro_build (&offset_expr, "bc", B_FMT); + break; + + /* The jal instructions must be handled as macros because when + generating PIC code they expand to multi-instruction + sequences. Normally they are simple instructions. */ + case M_JAL_A: + if (nanomips_opts.pic == NO_PIC) + macro_build (&offset_expr, "jal", B_FMT); + else + { + macro_build (&offset_expr, ADDRESS_LOAD_INSN, LWGP_FMT, + AT, BFD_RELOC_NANOMIPS_GOT_CALL, nanomips_gp_register); + + if (nanomips_linkrelax_p) + { + if (nanomips_opts.insn32) + macro_build (&offset_expr, "jalrc", "s,-i", AT, + BFD_RELOC_NANOMIPS_JALR32); + else + macro_build (&offset_expr, "jalrc", "mp,-i", AT, + BFD_RELOC_NANOMIPS_JALR16); + } + else + macro_build (NULL, "jalrc", (nanomips_opts.insn32 ? "s" : "mp"), + AT); + } + + break; + + case M_LB_AC: + case M_LBU_AC: + case M_LH_AC: + case M_LHU_AC: + case M_LW_AC: + fmt = ISA_UNSIGNED_LDST_FMT; + switch (s[1]) + { + case 'b': + gpfmt = "t,+1(ma)"; + break; + case 'd': + gpfmt = "t,+4(ma)"; + break; + case 'h': + gpfmt = "t,+3(ma)"; + break; + default: + if (s[2] == 'u') + gpfmt = "t,+2(ma)"; + else + gpfmt = "t,.(ma)"; + break; + } + goto ld; + + case M_LL_AC: + fmt = LL_SC_FMT; + align = 4; + offbits = ISA_SIGNED_OFFBITS; + goto ld; + + ld: + if (op[2] == op[0] + lp) + goto ld_st; + else + tempreg = op[0] + lp; + goto ld_noat; + + case M_LBUE_AC: + case M_LHUE_AC: + case M_LBE_AC: + case M_LHE_AC: + case M_SHE_AC: + case M_SWE_AC: + case M_LWE_AC: + case M_SBE_AC: + fmt = ISA_SIGNED_LDST_FMT; + goto ld_st_signed_off; + + case M_SB_AC: + case M_SH_AC: + fmt = ISA_UNSIGNED_LDST_FMT; + gpfmt = "t,+1(ma)"; + goto ld_st; + + case M_SW_AC: + fmt = ISA_UNSIGNED_LDST_FMT; + gpfmt = "t,.(ma)"; + goto ld_st; + + case M_LLE_AC: + case M_SCE_AC: + case M_SC_AC: + fmt = LL_SC_FMT; + align = 4; + goto ld_st_signed_off; + + case M_CACHE_AC: + case M_PREF_AC: + case M_CACHEE_AC: + case M_PREFE_AC: + fmt = PREF_FMT; + goto ld_st_signed_off; + + case M_SWM_AC: + case M_SDM_AC: + case M_LWM_AC: + case M_LDM_AC: + fmt = ISA_SIGNED_LDST_FMT ",|"; + + ld_st_signed_off: + offbits = ISA_SIGNED_OFFBITS; + ld_st: + tempreg = AT; + ld_noat: + breg = op[2]; + macro_ld_st (s, fmt, op, gpfmt, align, offbits, breg, tempreg, + &used_at, coproc); + break; + + case M_JRADDIUSP: + offset_expr = imm_expr; + if (small_poffset_p (0, 1, ISA_ADD_OFFBITS)) + macro_build (&imm_expr, "addiu", ADDIU_FMT, SP, SP, + BFD_RELOC_NANOMIPS_IMM16); + else if (small_noffset_p (0, ISA_OFFBITS)) + macro_build (&imm_expr, "addiu", ADDIU_FMT, SP, SP, + BFD_RELOC_NANOMIPS_NEG12); + else + { + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, "addu", "d,v,t", SP, SP, AT); + } + macro_build (NULL, "jrc", (nanomips_opts.insn32 ? "s" : "mp"), RA); + break; + + case M_LI: + if (imm_expr.X_op != O_absent) + load_register (op[0], &imm_expr, 0); + else + macro_la (op, 0, &used_at, false); + break; + + case M_LI_S: + if (imm_expr.X_op == O_constant) + { + load_register (op[0], &imm_expr, 0); + break; + } + + /* Loading from data section, fall-back to load-from-label + macro expansion. */ + used_at = 1; + align = 4; + offbits = ISA_OFFBITS; + fmt = ISA_UNSIGNED_LDST_FMT; + op[2] = 0; + s = "lw"; + gpfmt = LWGP_FMT; + tempreg = (op[0] != 0) ? op[0] : AT; + goto ld_noat; + + case M_LI_D: + /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits + wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high + order 32 bits of the value and the low order 32 bits are either + zero or in OFFSET_EXPR. */ + if (imm_expr.X_op == O_constant) + { + int hreg, lreg; + + if (target_big_endian) + { + hreg = op[0]; + lreg = op[0] + 1; + } + else + { + hreg = op[0] + 1; + lreg = op[0]; + } + + if (hreg <= 31) + load_register (hreg, &imm_expr, 0); + if (lreg <= 31) + { + if (offset_expr.X_op == O_absent) + move_register (lreg, 0); + else + { + gas_assert (offset_expr.X_op == O_constant); + load_register (lreg, &offset_expr, 0); + } + } + break; + } + + /* Loading from data section, fall-back to load-from-label + macro expansion. */ + gas_assert (imm_expr.X_op == O_absent); + used_at = 1; + align = 4; + offbits = ISA_OFFBITS; + fmt = ISA_UNSIGNED_LDST_FMT; + op[2] = 0; + s = "lw"; + gpfmt = LWGP_FMT; + tempreg = op[0]; + goto ldd_std; + + case M_LD_AC: + fmt = ISA_UNSIGNED_LDST_FMT; + gpfmt = "t,+2(ma)"; + s = "lw"; + goto ldd_std; + + case M_SD_AC: + fmt = ISA_UNSIGNED_LDST_FMT; + gpfmt = "t,+2(ma)"; + s = "sw"; + + ldd_std: + breg = op[2]; + macro_ldd_std (s, fmt, op, align, offbits, breg, &used_at); + break; + + case M_MUL: + macro_build (NULL, "multu", "s,t", op[1], op[2]); + macro_build (NULL, "mflo", MFHL_FMT, op[0]); + break; + + case M_MUL_I: + used_at = 1; + load_register (AT, &imm_expr, dbl); + macro_build (NULL, "mul", "d,v,t", op[0], op[1], AT); + break; + + case M_ROL: + if (op[0] == op[1]) + { + tempreg = AT; + used_at = 1; + } + else + tempreg = op[0]; + macro_build (NULL, "negu", "d,w", tempreg, op[2]); + macro_build (NULL, "rorv", BITW_FMT, op[0], op[1], tempreg); + break; + + case M_ROL_I: + { + unsigned int rot; + + rot = imm_expr.X_add_number & 0x1f; + macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], (32 - rot) & 0x1f); + break; + } + + case M_ROR_I: + { + unsigned int rot; + + rot = imm_expr.X_add_number & 0x1f; + macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], rot); + break; + } + + case M_SEQ: + if (op[1] == 0) + macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[2], + BFD_RELOC_LO16); + else if (op[2] == 0) + macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + else + { + macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]); + macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + } + break; + + case M_SEQ_I: + if (imm_expr.X_add_number == 0) + macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + else if (op[1] == 0) + { + as_warn (_("instruction %s: result is always false"), + ip->insn_mo->name); + move_register (op[0], 0); + } + else if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0) + macro_build (&imm_expr, "seqi", OP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + else + { + if (offset_high_unsigned (-imm_expr.X_add_number, + ISA_ADD_OFFBITS) == 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build (&imm_expr, "addiu", ADDIU_FMT, + op[0], op[1], BFD_RELOC_LO16); + } + else + { + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT); + } + macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + } + break; + + case M_SGE: /* X >= Y <==> not (X < Y) */ + s = "slt"; + goto sge; + case M_SGEU: + s = "sltu"; + sge: + macro_build (NULL, s, "d,v,t", op[0], op[1], op[2]); + macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + break; + + case M_SGE_I: /* X >= I <==> not (X < I) */ + case M_SGEU_I: + if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0) + macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", + OP_IMM_FMT, op[0], op[1], BFD_RELOC_LO16); + else + { + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t", + op[0], op[1], AT); + } + macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + break; + + case M_SGT: /* X > Y <==> Y < X */ + s = "slt"; + goto sgt; + case M_SGTU: + s = "sltu"; + sgt: + macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]); + break; + + case M_SGT_I: /* X > I <==> I < X */ + s = "slt"; + goto sgti; + case M_SGTU_I: + s = "sltu"; + sgti: + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, s, "d,v,t", op[0], AT, op[1]); + break; + + case M_SLE: /* X <= Y <==> Y >= X <==> not (Y < X) */ + s = "slt"; + goto sle; + case M_SLEU: + s = "sltu"; + sle: + macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]); + macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + break; + + case M_SLE_I: /* X <= I <==> I >= X <==> not (I < X) */ + s = "slt"; + goto slei; + case M_SLEU_I: + s = "sltu"; + slei: + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, s, "d,v,t", op[0], AT, op[1]); + macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0], + BFD_RELOC_LO16); + break; + + case M_SLT_I: + if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0) + { + macro_build (&imm_expr, "slti", OP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + break; + } + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, "slt", "d,v,t", op[0], op[1], AT); + break; + + case M_SLTU_I: + if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0) + { + macro_build (&imm_expr, "sltiu", OP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + break; + } + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, "sltu", "d,v,t", op[0], op[1], AT); + break; + + case M_SNE: + if (op[1] == 0) + macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[2]); + else if (op[2] == 0) + macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]); + else + { + macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]); + macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]); + } + break; + + case M_SNE_I: + if (imm_expr.X_add_number == 0) + macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]); + else if (op[1] == 0) + { + as_warn (_("instruction %s: result is always true"), + ip->insn_mo->name); + load_register (op[0], &expr1, GPR_SIZE == 64); + } + else + { + if (offset_high_unsigned (imm_expr.X_add_number, + ISA_ADD_OFFBITS) == 0) + macro_build (&imm_expr, "xori", BITOP_IMM_FMT, op[0], op[1], + BFD_RELOC_LO16); + else if (offset_high_unsigned (-imm_expr.X_add_number, + ISA_ADD_OFFBITS) == 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build (&imm_expr, "addiu", ADDIU_FMT, op[0], op[1], + BFD_RELOC_LO16); + } + else + { + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]); + } + macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]); + } + break; + + case M_SUB_I: + s = "addiu"; + s2 = "sub"; + goto do_subi_i; + case M_SUBU_I: + s = "addiu"; + s2 = "subu"; + if (offset_high_unsigned (-imm_expr.X_add_number, 16) == 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build (&imm_expr, s, ADDIU_FMT, op[0], op[1], + BFD_RELOC_NANOMIPS_IMM16); + break; + } + else if (offset_high_unsigned (imm_expr.X_add_number, 12) == 0) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build (&imm_expr, s, "t,r,h", op[0], op[1], + BFD_RELOC_NANOMIPS_NEG12); + break; + } + else if ((nanomips_opts.ase & ASE_xNMS) != 0 + && op[0] == op[1] + && !nanomips_opts.insn32) + { + imm_expr.X_add_number = -imm_expr.X_add_number; + macro_build (&imm_expr, s, "mp,mt,+R", op[0], op[0], + BFD_RELOC_NANOMIPS_I32); + break; + } + + do_subi_i: + used_at = 1; + load_register (AT, &imm_expr, dbl); + macro_build (NULL, s2, "d,v,t", op[0], op[1], AT); + break; + + case M_TEQ_I: + case M_TNE_I: + used_at = 1; + load_register (AT, &imm_expr, GPR_SIZE == 64); + macro_build (NULL, s, "s,t", op[0], AT); + break; + + case M_ULH_AC: + s = "ualh"; + goto uld_st; + + case M_ULW_AC: + s = "ualw"; + goto uld_st; + + case M_USH_AC: + s = "uash"; + goto uld_st; + + case M_USW_AC: + s = "uasw"; + goto uld_st; + + case M_ULD_AC: + s = "uald"; + s2 = "ualw"; + goto uld_st; + + case M_USD_AC: + s = "uasd"; + s2 = "uasw"; + + uld_st: + fmt = ISA_SIGNED_LDST_FMT; + offbits = ISA_SIGNED_OFFBITS; + if (s[3] == 'd' && GPR_SIZE == 32) + { + s = s2; + goto ldd_std; + } + else + goto ld_st; + break; + + default: + as_bad (_("macro %s not implemented yet"), ip->insn_mo->name); + break; + } + if (nanomips_opts.noat && used_at) + as_bad (_("macro used $at after \".set noat\"")); +} + +/* Look up instruction [START, START + LENGTH) in HASH. Record any extra + opcode bits in *OPCODE_EXTRA. */ + +static struct nanomips_opcode * +nanomips_lookup_insn (htab_t hash, const char *start, + ssize_t length, + unsigned int *opcode_extra ATTRIBUTE_UNUSED) +{ + char *name, *dot; + unsigned int suffix; + ssize_t opend; + struct nanomips_opcode *insn; + + /* Make a copy of the instruction so that we can fiddle with it. */ + name = xstrndup (start, length); + + forced_insn_format = (strchr (start, '[') != NULL + && strchr (strchr (start, '['), ']') != NULL); + + /* Look up the instruction as-is. */ + insn = str_hash_find (hash, name); + if (insn) + return insn; + + if (strchr (name, ']')) + dot = strchr (strchr (name, ']'), '.'); + else + dot = strchr (name, '.'); + + /* See if there's an instruction size override suffix, + either `16' or `32', at the end of the mnemonic proper, + that defines the operation, i.e. before the first `.' + character if any. Strip it and retry. */ + opend = dot != NULL ? dot - name : length; + if (opend >= 3 && name[opend - 2] == '1' && name[opend - 1] == '6') + suffix = 2; + else if (opend >= 2 && name[opend - 2] == '3' && name[opend - 1] == '2') + suffix = 4; + else if (opend >= 3 && name[opend - 2] == '4' && name[opend - 1] == '8') + suffix = 6; + else + suffix = 0; + if (suffix) + { + memmove (name + opend - 2, name + opend, length - opend + 1); + insn = str_hash_find (hash, name); + while (insn) + if (insn_length (insn) == suffix) + { + forced_insn_length = suffix; + return insn; + } + else if (forced_insn_format) + break; + else + insn++; + } + return NULL; +} + +/* Assemble an instruction into its binary format. If the instruction + is a macro, set imm_expr and offset_expr to the values associated + with "I" and "A" operands respectively. Otherwise store the value + of the relocatable field (if any) in offset_expr. In both cases + set offset_reloc to the relocation operators applied to offset_expr. */ + +static void +nanomips_ip (char *str, struct nanomips_cl_insn *insn) +{ + const struct nanomips_opcode *first, *past; + htab_t hash; + char format; + size_t end; + struct nanomips_operand_token *tokens; + unsigned int opcode_extra; + + hash = nanomips_op_hash; + past = &nanomips_opcodes[bfd_nanomips_num_opcodes]; + forced_insn_length = 0; + forced_insn_format = false; + opcode_extra = 0; + + /* We first try to match an instruction up to a space or to the end. */ + for (end = 0; !is_end_of_stmt (str[end]) && !is_whitespace (str[end]); end++) + continue; + + first = nanomips_lookup_insn (hash, str, end, &opcode_extra); + if (first == NULL) + { + set_insn_error (0, _("unrecognized opcode")); + return; + } + + if (strcmp (first->name, "li.s") == 0) + format = 'f'; + else if (strcmp (first->name, "li.d") == 0) + format = 'd'; + else + format = 0; + tokens = nanomips_parse_arguments (str + end, format); + if (!tokens) + return; + + if (!match_insns (insn, first, past, tokens, opcode_extra, false) + && !match_insns (insn, first, past, tokens, opcode_extra, true)) + set_insn_error (0, _("invalid operands")); + + obstack_free (&nanomips_operand_tokens, tokens); +} + +struct percent_op_match +{ + const char *str; + bfd_reloc_code_real_type reloc; +}; + +static const struct percent_op_match nanomips_percent_op[] = { + {"%lo", BFD_RELOC_NANOMIPS_LO12}, + {"%call16", BFD_RELOC_NANOMIPS_GOT_CALL}, + {"%got_call", BFD_RELOC_NANOMIPS_GOT_CALL}, + {"%got_disp", BFD_RELOC_NANOMIPS_GOT_DISP}, + {"%got_page", BFD_RELOC_NANOMIPS_GOT_PAGE}, + {"%got_ofst", BFD_RELOC_NANOMIPS_GOT_OFST}, + {"%got", BFD_RELOC_NANOMIPS_GOT_DISP}, + {"%gp_rel", BFD_RELOC_NANOMIPS_GPREL18}, + {"%gprel", BFD_RELOC_NANOMIPS_GPREL18}, + {"%gprel_hi", BFD_RELOC_NANOMIPS_GPREL_HI20}, + {"%gprel_lo", BFD_RELOC_NANOMIPS_GPREL_LO12}, + {"%gprel32", BFD_RELOC_NANOMIPS_GPREL_I32}, + {"%hi", BFD_RELOC_NANOMIPS_HI20}, + {"%pcrel_hi", BFD_RELOC_NANOMIPS_PCREL_HI20}, + {"%pcrel_lo", BFD_RELOC_NANOMIPS_LO12}, + {"%got_pcrel_hi", BFD_RELOC_NANOMIPS_GOTPC_HI20}, + {"%got_pcrel_lo", BFD_RELOC_NANOMIPS_GOT_LO12}, + {"%got_lo", BFD_RELOC_NANOMIPS_GOT_LO12}, + {"%got_pcrel32", BFD_RELOC_NANOMIPS_GOTPC_I32}, + {"%tlsgd", BFD_RELOC_NANOMIPS_TLS_GD}, + {"%tlsld", BFD_RELOC_NANOMIPS_TLS_LD}, + {"%dtprel", BFD_RELOC_NANOMIPS_TLS_DTPREL12}, + {"%tprel", BFD_RELOC_NANOMIPS_TLS_TPREL12}, + {"%gottprel", BFD_RELOC_NANOMIPS_TLS_GOTTPREL}, + {"%gottprel_pc32", BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32}, + /* These are currently not supported for nanoMIPS. */ + {"%call_hi", BFD_RELOC_UNUSED}, + {"%call_lo", BFD_RELOC_UNUSED} +}; + +/* Return true if *STR points to a relocation operator. When returning true, + move *STR over the operator and store its relocation code in *RELOC. + Leave both *STR and *RELOC alone when returning false. */ + +static bool +parse_relocation (char **str, bfd_reloc_code_real_type *reloc) +{ + const struct percent_op_match *percent_op; + size_t limit, i; + + percent_op = nanomips_percent_op; + limit = ARRAY_SIZE (nanomips_percent_op); + + for (i = 0; i < limit; i++) + if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) + == 0) + { + int len = strlen (percent_op[i].str); + + if (!is_end_of_stmt ((*str)[len]) + && !is_whitespace ((*str)[len]) + && (*str)[len] != '(') + continue; + + *str += strlen (percent_op[i].str); + *reloc = percent_op[i].reloc; + + /* Check whether the output BFD supports this relocation. + If not, issue an error and fall back on something safe. */ + if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc)) + { + as_bad (_("relocation %s isn't supported by the current ABI"), + percent_op[i].str); + *reloc = BFD_RELOC_UNUSED; + } + return true; + } + return false; +} + +/* Parse string STR as a 16-bit relocatable operand. Store the + expression in *EP and the relocations in the array starting + at RELOC. Return the number of relocation operators used. + + On exit, EXPR_END points to the first character after the expression. */ + +static size_t +my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, + char *str) +{ + bfd_reloc_code_real_type reversed_reloc[3]; + size_t reloc_index, i; + int crux_depth, str_depth; + char *crux; + + memset (reversed_reloc, 0, sizeof (reversed_reloc)); + /* Search for the start of the main expression, recoding relocations + in REVERSED_RELOC. End the loop with CRUX pointing to the start + of the main expression and with CRUX_DEPTH containing the number + of open brackets at that point. */ + reloc_index = -1; + str_depth = 0; + do + { + reloc_index++; + crux = str; + crux_depth = str_depth; + + /* Skip over whitespace and brackets, keeping count of the number + of brackets. */ + while (is_whitespace (*str) || *str == '(') + if (*str++ == '(') + str_depth++; + } + while (*str == '%' + && reloc_index < 1 + && parse_relocation (&str, &reversed_reloc[reloc_index])); + + my_getExpression (ep, crux); + str = expr_parse_end; + + /* Match every open bracket. */ + while (crux_depth > 0 && (*str == ')' || is_whitespace (*str))) + if (*str++ == ')') + crux_depth--; + + if (crux_depth > 0) + as_bad (_("unclosed '('")); + + expr_parse_end = str; + + for (i = 0; i < reloc_index; i++) + reloc[i] = reversed_reloc[reloc_index - 1 - i]; + + return reloc_index; +} + +static void +my_getExpression (expressionS *ep, char *str) +{ + char *save_in; + + save_in = input_line_pointer; + input_line_pointer = str; + expression (ep); + expr_parse_end = input_line_pointer; + input_line_pointer = save_in; +} + +const char * +md_atof (int type, char *litP, int *sizeP) +{ + return ieee_md_atof (type, litP, sizeP, target_big_endian); +} + +void +md_number_to_chars (char *buf, valueT val, int n) +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +static int +support_64bit_objects (void) +{ + const char **list, **l; + int yes; + + list = bfd_target_list (); + for (l = list; *l != NULL; l++) + if (strcmp (*l, ELF_NTARGET ("elf64-", "big")) == 0 + || strcmp (*l, ELF_NTARGET ("elf64-", "little")) == 0) + break; + yes = (*l != NULL); + free (list); + return yes; +} + +/* Set STRING_PTR (either &nanomips_arch_string or &nanomips_tune_string) to + NEW_VALUE. Warn if another value was already specified. Note: + we have to defer parsing the -march and -mtune arguments in order + to handle 'from-abi' correctly, since the ABI might be specified + in a later argument. */ + +static void +nanomips_set_option_string (const char **string_ptr, const char *new_value) +{ + if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0) + as_warn (_("a different %s was already specified, is now %s"), + string_ptr == &nanomips_arch_string ? "-march" : "-mtune", + new_value); + + *string_ptr = new_value; +} + +static void +nanomips_set_mcmodel (enum mc_model_type *model, const char *arg) +{ + if (strcmp (arg, "large")) + *model = MC_LARGE; + else if (strcmp (arg, "medium")) + *model = MC_LARGE; + else if (strcmp (arg, "auto")) + *model = MC_AUTO; + else + as_fatal (_("Request for unsupported memory model %s"), arg); +} + +int +md_parse_option (int c, const char *arg) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++) + if (c == nanomips_ases[i].option_on || c == nanomips_ases[i].option_off) + { + file_ase_explicit |= + nanomips_set_ase (&nanomips_ases[i], &file_nanomips_opts, + c == nanomips_ases[i].option_on); + return 1; + } + + switch (c) + { + case OPTION_CONSTRUCT_FLOATS: + nanomips_disable_float_construction = 0; + break; + + case OPTION_NO_CONSTRUCT_FLOATS: + nanomips_disable_float_construction = 1; + break; + + case OPTION_TRAP: + nanomips_trap = 1; + break; + + case OPTION_BREAK: + nanomips_trap = 0; + break; + + case OPTION_EB: + target_big_endian = 1; + break; + + case OPTION_EL: + target_big_endian = 0; + break; + + case 'g': + if (arg == NULL) + nanomips_debug = 2; + else + nanomips_debug = atoi (arg); + break; + + case OPTION_MTUNE: + nanomips_set_option_string (&nanomips_tune_string, arg); + break; + + case OPTION_MARCH: + nanomips_set_option_string (&nanomips_arch_string, arg); + break; + + case OPTION_NO_MICROMIPS: + case OPTION_NO_MIPS16: + break; + + case OPTION_INSN32: + file_nanomips_opts.insn32 = true; + break; + + case OPTION_NO_INSN32: + file_nanomips_opts.insn32 = false; + break; + + case OPTION_PIC: + file_nanomips_opts.pic = SVR4_PIC; + break; + + case OPTION_NOPIC: + file_nanomips_opts.pic = NO_PIC; + break; + + case OPTION_LARGE_PIC: + file_nanomips_opts.pic = SVR4_LARGE_PIC; + break; + + case 'G': + g_switch_value = atoi (arg); + g_switch_seen = 1; + break; + + /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32 + and -mabi=64. */ + case OPTION_32: + case OPTION_64: + as_fatal (_("Request for unsupported ABI")); + break; + + case OPTION_M32: + nanomips_abi = P32_ABI; + break; + + case OPTION_M64: + nanomips_abi = P64_ABI; + if (!support_64bit_objects ()) + as_fatal (_("no compiled in support for 64 bit object file format")); + break; + + case OPTION_BALC_STUBS: + file_nanomips_opts.no_balc_stubs = false; + break; + + case OPTION_NO_BALC_STUBS: + file_nanomips_opts.no_balc_stubs = true; + break; + + case OPTION_LEGACY_REGS: + file_nanomips_opts.legacyregs = true; + break; + + case OPTION_NO_LEGACY_REGS: + file_nanomips_opts.legacyregs = false; + break; + + case OPTION_LINKRELAX: + s_linkrelax (0); + break; + + case OPTION_SINGLE_FLOAT: + file_nanomips_opts.single_float = 1; + break; + + case OPTION_DOUBLE_FLOAT: + file_nanomips_opts.single_float = 0; + break; + + case OPTION_SOFT_FLOAT: + file_nanomips_opts.soft_float = 1; + break; + + case OPTION_HARD_FLOAT: + file_nanomips_opts.soft_float = 0; + break; + + case OPTION_PCREL: + file_nanomips_opts.pcrel = true; + break; + + case OPTION_NO_PCREL: + file_nanomips_opts.pcrel = false; + break; + + case OPTION_PID: + file_nanomips_opts.pid = true; + break; + + case OPTION_NO_PID: + file_nanomips_opts.pid = false; + break; + + case OPTION_MCMODEL: + nanomips_set_mcmodel (&file_nanomips_opts.mc_model, arg); + break; + + default: + return 0; + } + + return 1; +} + +/* Set up globals to tune for the ISA or processor described by INFO. */ + +static void +nanomips_set_tune (const struct nanomips_cpu_info *info) +{ + if (info != 0) + nanomips_tune = info->cpu; +} + +void +nanomips_after_parse_args (void) +{ + const struct nanomips_cpu_info *arch_info = 0; + const struct nanomips_cpu_info *tune_info = 0; + + if (nanomips_arch_string != 0) + arch_info = nanomips_parse_cpu ("-march", nanomips_arch_string); + + if (arch_info == 0) + { + arch_info = nanomips_parse_cpu ("default CPU", + NANOMIPS_CPU_STRING_DEFAULT); + gas_assert (arch_info); + } + + if (nanomips_abi == NO_ABI) + nanomips_abi = NANOMIPS_DEFAULT_ABI; + + if (ABI_NEEDS_64BIT_REGS (nanomips_abi) + && !ISA_HAS_64BIT_REGS (arch_info->isa)) + as_bad (_("-march=%s is not compatible with the selected ABI"), + arch_info->name); + + file_nanomips_opts.arch = arch_info->cpu; + file_nanomips_opts.isa = arch_info->isa; + file_nanomips_opts.init_ase = arch_info->ase; + + /* Set up initial nanomips_opts state. */ + nanomips_opts = file_nanomips_opts; + + /* Optimize for file_nanomips_opts.arch, unless -mtune selects a different + processor. */ + if (nanomips_tune_string != 0) + tune_info = nanomips_parse_cpu ("-mtune", nanomips_tune_string); + + if (tune_info == 0) + nanomips_set_tune (arch_info); + else + nanomips_set_tune (tune_info); +} + +/* This is called before the symbol table is processed. In order to + work with gcc when using mips-tfile, we must keep all local labels. + However, in other cases, we want to discard them. If we were + called with -g, but we didn't see any debugging information, it may + mean that gcc is smuggling debugging information through to + mips-tfile, in which case we must generate all local labels. */ + +void +nanomips_frob_file_before_adjust (void) +{ +#ifndef NO_ECOFF_DEBUGGING + if (ECOFF_DEBUGGING && nanomips_debug != 0 && !ecoff_debugging_seen) + flag_keep_locals = 1; +#endif +} + +static bool +frag_in_section_p (fragS *fragp, const char *secname) +{ + asection *sec = bfd_get_section_by_name (stdoutput, secname); + + while (sec != NULL) + { + fragS *iter = seg_info (sec)->frchainP->frch_root; + while (iter != NULL) + { + if (iter == fragp) + return true; + iter = iter->fr_next; + } + sec = bfd_get_next_section_by_name (NULL, sec); + } + return false; +} + +int +nanomips_force_relocation (fixS *fixp) +{ + if (generic_force_reloc (fixp)) + return 1; + + if (linkrelax_reloc_p (fixp->fx_r_type) + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_INSN32 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_INSN16 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_SAVERESTORE + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_JALR32 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_JALR16) + return 1; + + /* and for nanoMIPS expansions */ + if (pcrel_branch_reloc_p (fixp->fx_r_type) + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_PCREL_HI20 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_PC_I32 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOTPC_HI20 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOTPC_I32 + || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOT_LO12) + return 1; + + if (linkrelax + && fixp->fx_subsy + && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE) != 0 + && (S_GET_SEGMENT (fixp->fx_subsy)->flags & SEC_CODE) != 0) + { + /* For lable-difference relocations in .debug_info sections, we + want to force a relocation, even if the difference is link-time + invariant so that the linker has the opportunity to zero-out + AT_high_pc expressions when the corresponding sections get + garbage-collected. Spurious non-zero AT_high_pc fields of + garbage-collected sections can trip-up GDB source disassembly. */ + if (frag_in_section_p (fixp->fx_frag, ".debug_info")) + return 1; + else + return (!nanomips_allow_local_subtract_symbols (fixp->fx_addsy, + fixp->fx_subsy, + false)); + } + return 0; +} + +/* Find the size of an instruction relocated by RELOC, default to 4. */ +static unsigned int +get_reloc_size (bfd_reloc_code_real_type reloc) +{ + if (pcrel16_reloc_p (reloc)) + return 2; + else + return 4; +} + +/* Read the instruction associated with RELOC from BUF. */ + +static unsigned int +read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc) +{ + return read_compressed_insn (buf, get_reloc_size (reloc)); +} + +/* Write instruction INSN to BUF, given that it has been relocated + by RELOC. */ + +static void +write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc, + unsigned long insn) +{ + write_compressed_insn (buf, insn, get_reloc_size (reloc)); +} + +/* Apply a fixup to the object file. */ + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + char *buf; + unsigned long insn; + reloc_howto_type *howto = NULL; + + /* We ignore generic BFD relocations we don't know about. */ + howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); + if (! howto) + + if (fixP->fx_pcrel) + switch (fixP->fx_r_type) + { + case BFD_RELOC_NANOMIPS_PC_I32: + case BFD_RELOC_NANOMIPS_GOTPC_HI20: + case BFD_RELOC_NANOMIPS_GOTPC_I32: + case BFD_RELOC_NANOMIPS_GOT_LO12: + case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32: + break; + + case BFD_RELOC_8: + fixP->fx_r_type = BFD_RELOC_8_PCREL; + break; + } + + if (linkrelax_reloc_p (fixP->fx_r_type) + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_INSN32 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_INSN16 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_SAVERESTORE + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_JALR32 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_JALR16) + { + fixP->fx_addnumber = *valP; + return; + } + + gas_assert (fixP->fx_size == 2 + || fixP->fx_size == 4 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_I32 + || fixP->fx_r_type == BFD_RELOC_8 + || fixP->fx_r_type == BFD_RELOC_16 + || fixP->fx_r_type == BFD_RELOC_64 + || fixP->fx_r_type == BFD_RELOC_CTOR + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_NEG + || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_UNSIGNED_8 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_SIGNED_8 + || fixP->fx_r_type == BFD_RELOC_NANOMIPS_ASHIFTR_1 + || (fixP->fx_size == 1 && fixP->fx_r_type == BFD_RELOC_32)); + + buf = fixP->fx_frag->fr_literal + fixP->fx_where; + + /* Don't treat parts of a composite relocation as done. There are two + reasons for this: + + (1) The second and third parts will be against 0 (RSS_UNDEF) but + should nevertheless be emitted if the first part is. + + (2) In normal usage, composite relocations are never assembly-time + constants. The easiest way of dealing with the pathological + exceptions is to generate a relocation against STN_UNDEF and + leave everything up to the linker. */ + if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0) + fixP->fx_done = 1; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_NANOMIPS_TLS_DTPREL: + case BFD_RELOC_NANOMIPS_TLS_TPREL: + case BFD_RELOC_NANOMIPS_TLS_GD: + case BFD_RELOC_NANOMIPS_TLS_LD: + case BFD_RELOC_NANOMIPS_TLS_GD_I32: + case BFD_RELOC_NANOMIPS_TLS_LD_I32: + case BFD_RELOC_NANOMIPS_TLS_DTPREL12: + case BFD_RELOC_NANOMIPS_TLS_DTPREL16: + case BFD_RELOC_NANOMIPS_TLS_DTPREL_I32: + case BFD_RELOC_NANOMIPS_TLS_TPREL12: + case BFD_RELOC_NANOMIPS_TLS_TPREL16: + case BFD_RELOC_NANOMIPS_TLS_TPREL_I32: + case BFD_RELOC_NANOMIPS_TLS_GOTTPREL: + case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32: + if (!fixP->fx_addsy) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("TLS relocation against a constant")); + break; + } + S_SET_THREAD_LOCAL (fixP->fx_addsy); + /* fall through */ + + case BFD_RELOC_HI16: + case BFD_RELOC_HI16_S: + case BFD_RELOC_LO16: + case BFD_RELOC_GPREL16: + case BFD_RELOC_GPREL32: + case BFD_RELOC_NANOMIPS_EH: + case BFD_RELOC_NANOMIPS_NEG: + case BFD_RELOC_NANOMIPS_GOT_CALL: + case BFD_RELOC_NANOMIPS_GOT_DISP: + case BFD_RELOC_NANOMIPS_GOT_PAGE: + case BFD_RELOC_NANOMIPS_GOT_OFST: + case BFD_RELOC_NANOMIPS_GPREL19_S2: + case BFD_RELOC_NANOMIPS_GPREL18_S3: + case BFD_RELOC_NANOMIPS_GPREL18: + case BFD_RELOC_NANOMIPS_GPREL16_S2: + case BFD_RELOC_NANOMIPS_GPREL7_S2: + case BFD_RELOC_NANOMIPS_GPREL17_S1: + case BFD_RELOC_NANOMIPS_GOT_LO12: + if (fixP->fx_done) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("unsupported constant in relocation")); + break; + + case BFD_RELOC_64: + /* This is handled like BFD_RELOC_32, but we output a sign + extended value if we are only 32 bits. */ + if (fixP->fx_done) + { + if (8 <= sizeof (valueT)) + md_number_to_chars (buf, *valP, 8); + else + { + valueT hiv; + + if ((*valP & 0x80000000) != 0) + hiv = 0xffffffff; + else + hiv = 0; + md_number_to_chars (buf + (target_big_endian ? 4 : 0), + *valP, 4); + md_number_to_chars (buf + (target_big_endian ? 0 : 4), hiv, 4); + } + } + break; + + case BFD_RELOC_RVA: + case BFD_RELOC_32: + case BFD_RELOC_32_PCREL: + case BFD_RELOC_16: + case BFD_RELOC_8: + case BFD_RELOC_NANOMIPS_UNSIGNED_8: + case BFD_RELOC_NANOMIPS_UNSIGNED_16: + case BFD_RELOC_NANOMIPS_SIGNED_8: + case BFD_RELOC_NANOMIPS_SIGNED_16: + /* If we are deleting this reloc entry, we must fill in the + value now. This can happen if we have a .word which is not + resolved when it appears but is later defined. */ + if (fixP->fx_done) + md_number_to_chars (buf, *valP, fixP->fx_size); + break; + + case BFD_RELOC_NANOMIPS_ASHIFTR_1: + if (fixP->fx_done) + *valP = (*valP >> 1); + break; + + case BFD_RELOC_NANOMIPS_HI20: + case BFD_RELOC_NANOMIPS_GPREL_HI20: + if (!fixP->fx_done) + break; + + insn = read_reloc_insn (buf, fixP->fx_r_type); + + insn |= ((*valP >> 12) & 0x1ff) << 12 + | ((*valP >> 21) & 0x3ff) << 2 + | ((*valP >> 31) & 1); + + write_reloc_insn (buf, fixP->fx_r_type, insn); + break; + + case BFD_RELOC_NANOMIPS_I32: + case BFD_RELOC_NANOMIPS_GPREL_I32: + if (fixP->fx_done) + write_compressed_insn (buf, *valP & 0xffffffff, 4); + break; + + case BFD_RELOC_NANOMIPS_LO12: + case BFD_RELOC_NANOMIPS_GPREL_LO12: + if (fixP->fx_done) + { + insn = read_reloc_insn (buf, fixP->fx_r_type); + insn |= *valP & 0xfff; + write_reloc_insn (buf, fixP->fx_r_type, insn); + } + break; + + case BFD_RELOC_NANOMIPS_LO4_S2: + if (fixP->fx_done) + { + if ((*valP & 0xfc3) != 0) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("offset out of range (0x%lx)"), + (long) fixP->fx_offset); + insn = read_reloc_insn (buf, fixP->fx_r_type); + insn |= (*valP >> 2) & 0xf; + write_reloc_insn (buf, fixP->fx_r_type, insn); + } + break; + + case BFD_RELOC_NANOMIPS_NEG12: + if (fixP->fx_done) + { + insn = read_reloc_insn (buf, fixP->fx_r_type); + insn |= (-*valP) & 0xfff; + write_reloc_insn (buf, fixP->fx_r_type, insn); + } + break; + + case BFD_RELOC_NANOMIPS_IMM16: + if (fixP->fx_done) + { + insn = read_reloc_insn (buf, fixP->fx_r_type); + insn |= *valP & 0xffff; + write_reloc_insn (buf, fixP->fx_r_type, insn); + } + break; + + case BFD_RELOC_NANOMIPS_PC_I32: + case BFD_RELOC_NANOMIPS_PCREL_HI20: + case BFD_RELOC_NANOMIPS_GOTPC_I32: + case BFD_RELOC_NANOMIPS_GOTPC_HI20: + gas_assert (!fixP->fx_done); + break; + + case BFD_RELOC_NANOMIPS_4_PCREL_S1: + case BFD_RELOC_NANOMIPS_7_PCREL_S1: + case BFD_RELOC_NANOMIPS_10_PCREL_S1: + case BFD_RELOC_NANOMIPS_11_PCREL_S1: + case BFD_RELOC_NANOMIPS_14_PCREL_S1: + case BFD_RELOC_NANOMIPS_21_PCREL_S1: + case BFD_RELOC_NANOMIPS_25_PCREL_S1: + if (!fixP->fx_done) + break; + + case BFD_RELOC_VTABLE_INHERIT: + fixP->fx_done = 0; + if (fixP->fx_addsy + && !S_IS_DEFINED (fixP->fx_addsy) + && !S_IS_WEAK (fixP->fx_addsy)) + S_SET_WEAK (fixP->fx_addsy); + break; + + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + + default: + abort (); + } + + /* Remember value for tc_gen_reloc. */ + fixP->fx_addnumber = *valP; +} + +static symbolS * +get_symbol (void) +{ + int c; + char *name; + symbolS *p; + + c = get_symbol_name (&name); + p = symbol_find_or_make (name); + (void) restore_line_pointer (c); + return p; +} + +/* Create R_NANOMIPS_ALIGN to record the alignment request. Value of the + absolute symbol gives alignment requested. The relocation is created at + the start of padding bytes. Create R_NANOMIPS_FILL and R_NANOMIPS_MAX + to record fill value and maximum alignment respectively. */ + +static void +create_align_relocs (fragS *fragp, int align_to, unsigned int fill_value, + int fill_length, int max_fill) +{ + static int symidx = 1; + symbolS *sym; + char sname[30]; + fixS *fixp; + int where; + + if (fragp == frag_now) + where = frag_now_fix (); + else + where = fragp->fr_fix; + + /* The '\2' ensures that no other symbol will get the + same name as this. */ + sprintf (sname, "__reloc_align_\2_%d", symidx++); + sym = symbol_find (sname); + sym = symbol_new (sname, absolute_section, &zero_address_frag, align_to); + symbol_table_insert (sym); + + fixp = fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_ALIGN); + if (fragp->tc_frag_data == NULL) + fragp->tc_frag_data = fixp; + + /* Generate fill reloc. Default fill value is nop32. */ + if (fill_length) + { + sprintf (sname, "__reloc_fill_\2_%x", fill_value); + sym = symbol_find (sname); + if (sym == NULL) + { + sym = symbol_new (sname, absolute_section, &zero_address_frag, fill_value); + symbol_table_insert (sym); + elf_symbol (symbol_get_bfdsym (sym))->internal_elf_sym.st_size + = fill_length; + } + + fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_FILL); + } + + /* Generate max reloc. */ + if (max_fill != 0) + { + sprintf (sname, "__reloc_max_\2_%x", max_fill); + sym = symbol_find (sname); + if (sym == NULL) + { + sym = symbol_new (sname, absolute_section, &zero_address_frag, max_fill); + symbol_table_insert (sym); + } + fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_MAX); + } +} + +/* Align the current frag to a given power of two. If a particular + fill byte should be used, FILL points to an integer that contains + that byte, otherwise FILL is null. */ + +static void +nanomips_align (int to, int *fill, symbolS *label) +{ + nanomips_flush_pending_output (); + struct insn_label_list *l; + + if (fill == NULL && subseg_text_p (now_seg)) + frag_align_code (to, 0); + else + frag_align (to, fill ? *fill : 0, 0); + record_alignment (now_seg, to); + if (label != NULL) + { + gas_assert (S_GET_SEGMENT (label) == now_seg); + symbol_set_frag (label, frag_now); + S_SET_VALUE (label, (valueT) frag_now_fix ()); + } +} + +/* Align to a given power of two. .align 0 turns off the automatic + alignment used by the data creating pseudo-ops. */ + +static void +s_align (int x ATTRIBUTE_UNUSED) +{ + int temp, fill_value, *fill_ptr; + long max_alignment = 15; + + /* Note that the assembler pulls down any immediately preceding label + to the aligned address. */ + + temp = get_absolute_expression (); + if (temp > max_alignment) + as_bad (_("alignment too large, %d assumed"), temp = max_alignment); + else if (temp < 0) + { + as_warn (_("alignment negative, 0 assumed")); + temp = 0; + } + if (*input_line_pointer == ',') + { + ++input_line_pointer; + fill_value = get_absolute_expression (); + fill_ptr = &fill_value; + } + else + fill_ptr = 0; + if (temp) + { + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; + + if (nanomips_linkrelax_p + && (bfd_section_flags (now_seg) & SEC_CODE) != 0 + && temp > 1) + { + int fill_length = 0; + + if (fill_ptr == NULL && subseg_text_p (now_seg)) + frag_grow (MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max)); + else + { + fill_length = 1; + frag_grow (1); + } + + create_align_relocs (frag_now, temp, fill_value, fill_length, 0); + } + + /* Auto alignment should be switched on by next section change. */ + auto_align = 1; + nanomips_align (temp, fill_ptr, l != NULL ? l->label : NULL); + } + else + { + auto_align = 0; + } + demand_empty_rest_of_line (); +} + +static void +s_change_sec (int sec) +{ + segT seg; + + if ((now_seg->flags & SHF_EXECINSTR) != 0 + && stub_funcless_mode + && !nanomips_opts.no_balc_stubs + && stubg_now != NULL) + { + stub_funcless_mode = false; + stubgroup_wane (); + } + + /* The ELF backend needs to know that we are changing sections, so + that .previous works correctly. We could do something like check + for an obj_section_change_hook macro, but that might be confusing + as it would not be appropriate to use it in the section changing + functions in read.c, since obj-elf.c intercepts those. FIXME: + This should be cleaner, somehow. */ + obj_elf_section_change_hook (); + + nanomips_flush_pending_output (); + + switch (sec) + { + case 't': + s_text (0); + break; + case 'd': + s_data (0); + break; + case 'b': + subseg_set (bss_section, (subsegT) get_absolute_expression ()); + demand_empty_rest_of_line (); + break; + + case 'r': + seg = subseg_new (RDATA_SECTION_NAME, + get_absolute_expression ()); + bfd_set_section_flags (seg, (SEC_ALLOC | SEC_LOAD + | SEC_READONLY | SEC_RELOC + | SEC_DATA)); + if (strncmp (TARGET_OS, "elf", 3) != 0) + record_alignment (seg, 4); + demand_empty_rest_of_line (); + break; + + case 's': + seg = subseg_new (".sdata", get_absolute_expression ()); + bfd_set_section_flags (seg, + SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA); + if (strncmp (TARGET_OS, "elf", 3) != 0) + record_alignment (seg, 4); + demand_empty_rest_of_line (); + break; + + case 'B': + seg = subseg_new (".sbss", get_absolute_expression ()); + bfd_set_section_flags (seg, SEC_ALLOC); + if (strncmp (TARGET_OS, "elf", 3) != 0) + record_alignment (seg, 4); + demand_empty_rest_of_line (); + break; + } + auto_align = 1; +} + +void +s_change_section (int ignore ATTRIBUTE_UNUSED) +{ + char *saved_ilp; + const char *section_name; + char c, next_c = 0; + int section_type; + int section_flag; + int section_entry_size; + + saved_ilp = input_line_pointer; + section_name = obj_elf_section_name (); + if (section_name == NULL) + return; + c = input_line_pointer[0]; + if (c) + next_c = input_line_pointer[1]; + + if ((now_seg->flags & SHF_EXECINSTR) != 0 + && stub_funcless_mode + && !nanomips_opts.no_balc_stubs + && stubg_now != NULL) + { + stub_funcless_mode = false; + stubgroup_wane (); + } + + /* Do we have .section Name<,"flags">? */ + if (c != ',' || (c == ',' && next_c == '"')) + { + input_line_pointer = saved_ilp; + obj_elf_section (ignore); + return; + } + input_line_pointer++; + + /* Do we have .section Name<,type><,flag><,entry_size><,alignment> */ + if (c == ',') + section_type = get_absolute_expression (); + else + section_type = 0; + if (*input_line_pointer++ == ',') + section_flag = get_absolute_expression (); + else + section_flag = 0; + if (*input_line_pointer++ == ',') + section_entry_size = get_absolute_expression (); + else + section_entry_size = 0; + + section_name = xstrdup (section_name); + + if ((now_seg->flags & SHF_EXECINSTR) != 0 + && stub_funcless_mode + && !nanomips_opts.no_balc_stubs + && stubg_now != NULL) + { + stub_funcless_mode = false; + stubgroup_wane (); + } + obj_elf_change_section (section_name, section_type, section_flag, + section_entry_size, 0, false); +} + +void +nanomips_enable_auto_align (void) +{ + auto_align = 1; +} + +static void +s_cons (int log_size) +{ + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; + symbolS *label; + + label = l != NULL ? l->label : NULL; + nanomips_flush_pending_output (); + if (log_size > 0 + && auto_align) + nanomips_align (log_size, 0, label); + cons (1 << log_size); + nanomips_clear_insn_labels (); +} + +static void +s_sign_cons (int log_size) +{ + sign_cons = true; + s_cons (log_size); + sign_cons = false; +} + +static void +s_float_cons (int type) +{ + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; + symbolS *label; + + label = l != NULL ? l->label : NULL; + nanomips_flush_pending_output (); + + if (auto_align) + { + if (type == 'd') + nanomips_align (3, 0, label); + else + nanomips_align (2, 0, label); + } + float_cons (type); + nanomips_clear_insn_labels (); +} + +/* This structure is used to hold a stack of .set values. */ + +struct nanomips_option_stack +{ + struct nanomips_option_stack *next; + struct nanomips_set_options options; +}; + +static struct nanomips_option_stack *nanomips_opts_stack; + +static bool +parse_code_option (char *name) +{ + const struct nanomips_ase *ase; + if (strcmp (name, "at") == 0) + nanomips_opts.noat = 0; + else if (strcmp (name, "noat") == 0) + nanomips_opts.noat = 1; + else if (strcmp (name, "softfloat") == 0) + nanomips_opts.soft_float = 1; + else if (strcmp (name, "hardfloat") == 0) + nanomips_opts.soft_float = 0; + else if (strcmp (name, "singlefloat") == 0) + nanomips_opts.single_float = 1; + else if (strcmp (name, "doublefloat") == 0) + nanomips_opts.single_float = 0; + else if (strcmp (name, "nooddspreg") == 0) + as_bad (_("nanoMIPS allows odd single-precision FP registers")); + else if (strcmp (name, "mips16") == 0 || strcmp (name, "MIPS-16") == 0) + as_bad (_("nanoMIPS does not support MIPS16 ASE")); + else if (strcmp (name, "micromips") == 0) + as_bad (_("micromips is incompatible with nanoMIPS")); + else if (name[0] == 'n' + && name[1] == 'o' + && (ase = nanomips_lookup_ase (name + 2))) + nanomips_set_ase (ase, &nanomips_opts, false); + else if ((ase = nanomips_lookup_ase (name))) + nanomips_set_ase (ase, &nanomips_opts, true); + else if (strncmp (name, "mips", 4) == 0) + as_bad (_("cannot change ISA from nanoMIPS to %s"), name); + else if (strncmp (name, "arch=mips", 9) == 0) + as_bad (_("cannot change ISA from nanoMIPS to %s"), name + 5); + else if (strncmp (name, "arch=", 5) == 0) + { + /* Permit the user to change the ISA and architecture on the fly. + Needless to say, misuse can cause serious problems. */ + const struct nanomips_cpu_info *p; + + p = nanomips_parse_cpu ("internal use", name + 5); + if (!p) + as_bad (_("unknown architecture %s"), name + 5); + else + { + nanomips_opts.arch = p->cpu; + nanomips_opts.isa = p->isa; + nanomips_opts.init_ase = p->ase; + } + } + else if (strcmp (name, "insn32") == 0) + nanomips_opts.insn32 = true; + else if (strcmp (name, "noinsn32") == 0) + nanomips_opts.insn32 = false; + else if (strncmp (name, "mcmodel=", 8) == 0) + { + if (strcmp (name + 8, "auto") == 0) + nanomips_opts.mc_model = MC_AUTO; + else if (strcmp (name + 8, "medium") == 0) + nanomips_opts.mc_model = MC_MEDIUM; + else if (strcmp (name + 8, "large") == 0) + nanomips_opts.mc_model = MC_LARGE; + else + as_bad (_("invalid memory model setting %s"), name); + } + else if (strncmp (name, "pic", 3) == 0) + { + int i = atoi (name + 3); + if (i == 0) + nanomips_opts.pic = NO_PIC; + else if (i == 1) + nanomips_opts.pic = SVR4_PIC; + else if (i == 2) + nanomips_opts.pic = SVR4_LARGE_PIC; + else + as_bad (_(".set pic%d not supported"), i); + } + else if (strcmp (name, "nopic") == 0) + nanomips_opts.pic = NO_PIC; + else if (strcmp (name, "pid") == 0) + nanomips_opts.pid = true; + else if (strcmp (name, "nopic") == 0) + nanomips_opts.pid = false; + else if (strcmp (name, "pcrel") == 0) + nanomips_opts.pcrel = true; + else if (strcmp (name, "nopcrel") == 0) + nanomips_opts.pcrel = false; + else + return (strcmp (name, "oddspreg") == 0 + || strcmp (name, "nomips16") == 0 + || strcmp (name, "noMIPS-16") == 0 + || strcmp (name, "nomicromips") == 0 + || strcmp (name, "nobopt") == 0 + || strcmp (name, "noforbidden-slots") == 0 + || strcmp (name, "move") == 0 + || strcmp (name, "nomove") == 0 + || strcmp (name, "novolatile") == 0 + || strcmp (name, "volatile") == 0); + + return true; +} + +/* Handle the .set pseudo-op. */ + +static void +s_nanomipsset (int x ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer, ch; + int prev_isa = nanomips_opts.isa; + + file_check_options (); + + while (!is_end_of_stmt (*input_line_pointer)) + ++input_line_pointer; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (strchr (name, ',')) + { + /* Generic ".set" directive; use the generic handler. */ + *input_line_pointer = ch; + input_line_pointer = name; + s_set (0); + return; + } + + if (strcmp (name, "reorder") == 0) + { + if (nanomips_opts.noreorder) + end_noreorder (); + } + else if (strcmp (name, "noreorder") == 0) + { + if (!nanomips_opts.noreorder) + start_noreorder (); + } + else if (strcmp (name, "macro") == 0) + nanomips_opts.nomacro = 0; + else if (strcmp (name, "nomacro") == 0) + nanomips_opts.nomacro = 1; + else if (strcmp (name, "arch=default") == 0) + { + nanomips_opts.isa = file_nanomips_opts.isa; + nanomips_opts.arch = file_nanomips_opts.arch; + nanomips_opts.init_ase = file_nanomips_opts.init_ase; + nanomips_opts.gp = file_nanomips_opts.gp; + nanomips_opts.fp = file_nanomips_opts.fp; + } + else if (strcmp (name, "push") == 0) + { + struct nanomips_option_stack *s; + + s = (struct nanomips_option_stack *) xmalloc (sizeof *s); + s->next = nanomips_opts_stack; + s->options = nanomips_opts; + nanomips_opts_stack = s; + } + else if (strcmp (name, "pop") == 0) + { + struct nanomips_option_stack *s; + + s = nanomips_opts_stack; + if (s == NULL) + as_bad (_(".set pop with no .set push")); + else + { + /* If we're changing the reorder mode we need to handle + delay slots correctly. */ + if (s->options.noreorder && !nanomips_opts.noreorder) + start_noreorder (); + else if (!s->options.noreorder && nanomips_opts.noreorder) + end_noreorder (); + + nanomips_opts = s->options; + nanomips_opts_stack = s->next; + free (s); + } + } + else if (strcmp (name, "nolinkrelax") == 0) + toggle_linkrelax_p = (linkrelax && nanomips_linkrelax_p); + else if (strcmp (name, "linkrelax") == 0) + toggle_linkrelax_p = (linkrelax && !nanomips_linkrelax_p); + else if (strcmp (name, "legacyregs") == 0) + nanomips_opts.legacyregs = true; + else if (strcmp (name, "nolegacyregs") == 0) + nanomips_opts.legacyregs = false; + else if (!parse_code_option (name)) + as_warn (_("tried to set unrecognized symbol: %s\n"), name); + + /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp + registers based on what is supported by the arch/cpu. */ + if (nanomips_opts.isa != prev_isa) + { + switch (nanomips_opts.isa) + { + case 0: + break; + case ISA_NANOMIPS32R6: + nanomips_opts.gp = 32; + nanomips_opts.fp = 64; + break; + case ISA_NANOMIPS64R6: + nanomips_opts.gp = 64; + nanomips_opts.fp = 64; + break; + default: + as_bad (_("unknown ISA level %s"), name + 4); + break; + } + } + check_isa_supports_ases (); + *input_line_pointer = ch; + demand_empty_rest_of_line (); +} + +/* Handle the .module pseudo-op. */ + +static void +s_module (int ignore ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer, ch; + + while (!is_end_of_stmt (*input_line_pointer)) + ++input_line_pointer; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (!file_nanomips_opts_checked) + { + if (!parse_code_option (name)) + as_bad (_(".module used with unrecognized symbol: %s\n"), name); + + /* Update module level settings from nanomips_opts. */ + file_nanomips_opts = nanomips_opts; + } + else + as_bad (_(".module is not permitted after generating code")); + + *input_line_pointer = ch; + demand_empty_rest_of_line (); +} + +/* Handle the .cpsetup pseudo-op defined for NewABI PIC code. The syntax is: + .cpsetup $reg1, offset|$reg2, label */ + +static void +s_cpsetup (int ignore ATTRIBUTE_UNUSED) +{ + expressionS ex; + file_check_options (); + + /* If we are not generating SVR4 PIC code, .cpsetup is ignored. */ + if (nanomips_opts.pic == NO_PIC) + { + s_ignore (0); + return; + } + + ex.X_op = O_symbol; + ex.X_add_symbol = symbol_find_or_make ("_gp"); + ex.X_op_symbol = NULL; + ex.X_add_number = 0; + symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT; + + macro_start (); + macro_pcrel_la (nanomips_gp_register, &ex, BFD_RELOC_UNUSED); + macro_end (true); + ignore_rest_of_line (); + + return; +} + +/* Handle a .dtprelword, .dtpreldword, .tprelword, or .tpreldword + pseudo-op; DIRSTR says which. The pseudo-op generates a BYTES-size + DTP- or TP-relative relocation of type RTYPE, for use in DWARF + debug information. */ + +static void +s_tls_rel_directive (const size_t bytes, const char *dirstr, + bfd_reloc_code_real_type rtype) +{ + expressionS ex; + char *p; + + expression (&ex); + + if (ex.X_op != O_symbol) + { + as_bad (_("unsupported use of %s"), dirstr); + ignore_rest_of_line (); + } + + p = frag_more (bytes); + md_number_to_chars (p, 0, bytes); + fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, false, rtype); + demand_empty_rest_of_line (); +} + +/* Handle .dtprelword. */ + +static void +s_dtprelword (int ignore ATTRIBUTE_UNUSED) +{ + s_tls_rel_directive (4, ".dtprelword", BFD_RELOC_NANOMIPS_TLS_DTPREL); +} + +/* Handle .dtpreldword. */ + +static void +s_dtpreldword (int ignore ATTRIBUTE_UNUSED) +{ + s_tls_rel_directive (8, ".dtpreldword", BFD_RELOC_NANOMIPS_TLS_DTPREL); +} + +/* Handle the .ehword pseudo-op. This is used when generating unwinding + tables. It generates a PC32 reloc. */ + +static void +s_ehword (int ignore ATTRIBUTE_UNUSED) +{ + expressionS ex; + char *p; + + nanomips_flush_pending_output (); + + expression (&ex); + nanomips_clear_insn_labels (); + + if (ex.X_op != O_symbol || ex.X_add_number != 0) + { + as_bad (_("unsupported use of .ehword")); + ignore_rest_of_line (); + } + + p = frag_more (4); + md_number_to_chars (p, 0, 4); + fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, false, + BFD_RELOC_NANOMIPS_EH); + + demand_empty_rest_of_line (); +} + +/* Handle the .insn pseudo-op, retained for compatibility. */ + +static void +s_insn (int ignore ATTRIBUTE_UNUSED) +{ + demand_empty_rest_of_line (); +} + +/* Parse a register string into a number. Called from the ECOFF code + to parse .frame. The argument is non-zero if this is the frame + register, so that we can record it in nanomips_frame_reg. */ + +int +tc_get_register (int frame ATTRIBUTE_UNUSED) +{ + unsigned int reg; + + SKIP_WHITESPACE (); + if (!reg_lookup (&input_line_pointer, RWARN | RTYPE_NUM | RTYPE_GP, ®)) + reg = 0; + + return reg; +} + +/* Utility routine, called from above as well. If called while the + input file is still being read, it's only an approximation. (For + example, a symbol may later become defined which appeared to be + undefined earlier.) */ + +static int +nopic_need_relax (symbolS *sym, int before_relaxing) +{ + if (sym == 0) + return 0; + + if (g_switch_value > 0) + { + const char *symname; + int change; + + /* Find out whether this symbol can be referenced off the $gp + register. It can be if it is smaller than the -G size or if + it is in the .sdata or .sbss section. Certain symbols can + not be referenced off the $gp, although it appears as though + they can. */ + symname = S_GET_NAME (sym); + if (symname != (const char *) NULL + && (strcmp (symname, "eprol") == 0 + || strcmp (symname, "etext") == 0 + || strcmp (symname, "_gp") == 0 + || strcmp (symname, "edata") == 0 + || strcmp (symname, "_fbss") == 0 + || strcmp (symname, "_fdata") == 0 + || strcmp (symname, "_ftext") == 0 + || strcmp (symname, "end") == 0 + || strcmp (symname, "_gp_disp") == 0)) + change = 1; + else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym)) + && (0 +#ifndef NO_ECOFF_DEBUGGING + || (symbol_get_obj (sym)->ecoff_extern_size != 0 + && (symbol_get_obj (sym)->ecoff_extern_size + <= g_switch_value)) +#endif + /* We must defer this decision until after the whole + file has been read, since there might be a .extern + after the first use of this symbol. */ + || (before_relaxing +#ifndef NO_ECOFF_DEBUGGING + && symbol_get_obj (sym)->ecoff_extern_size == 0 +#endif + && S_GET_VALUE (sym) == 0) + || (S_GET_VALUE (sym) != 0 + && S_GET_VALUE (sym) <= g_switch_value))) + change = 0; + else + { + const char *segname; + segname = segment_name (S_GET_SEGMENT (sym)); + change = (strcmp (segname, ".sdata") != 0 + && strcmp (segname, ".sbss") != 0 + && strncmp (segname, ".sdata.", 7) != 0 + && strncmp (segname, ".sbss.", 6) != 0 + && strncmp (segname, ".gnu.linkonce.sb.", 17) != 0 + && strncmp (segname, ".gnu.linkonce.s.", 16) != 0); + } + return change; + } + else + /* We are not optimizing for the $gp register. */ + return 1; +} + +/* Return true if the given symbol should be considered local for SVR4 PIC. */ + +static bool +pic_need_relax (symbolS *sym, asection *segtype) +{ + asection *symsec; + + if (nanomips_opts.mc_model == MC_AUTO && linkrelax) + return false; + + /* Handle the case of a symbol equated to another symbol. */ + while (symbol_equated_reloc_p (sym)) + { + symbolS *n; + + /* It's possible to get a loop here in a badly written program. */ + n = symbol_get_value_expression (sym)->X_add_symbol; + if (n == sym) + break; + sym = n; + } + + if (symbol_section_p (sym)) + return true; + + symsec = S_GET_SEGMENT (sym); + + /* This must duplicate the test in adjust_reloc_syms. */ + return (!bfd_is_und_section (symsec) + && !bfd_is_abs_section (symsec) + && !bfd_is_com_section (symsec) + && !s_is_linkonce (sym, segtype) + /* A global or weak symbol is treated as external. */ + && !S_IS_WEAK (sym) + && !S_IS_EXTERNAL (sym)); +} + +/* Check if relocation in HEAD refers to an address immediately following + instruction in an adjacent frag. 16-bit conditional branches cannot + reach the immediate next instruction and must be relaxed. */ +static bool +nanomips_frag_match (fragS *head, fragS *matchP) +{ + bfd_vma val = S_GET_VALUE (head->fr_symbol); + + /* Typically, a match is as easy as checking that the frags are adjacent + and the symbol refered to is at the very beginning of the target frag. */ + if (matchP == head->fr_next && val == matchP->fr_address) + return true; + + /* The tricky part: when doing assembly listings all sorts of fillers are + generated between what would be adjacent frags. So we need to look + through the frag list, until we come to a non-empty frag that either + matches or exceeds our target. */ + if (listing) + { + head = head->fr_next; + while (head && head != matchP && head->fr_fix + head->fr_var == 0) + { + /* Unlikely, but just in case we came too far, bail out! */ + if (head->fr_line > matchP->fr_line) + break; + else + head = head->fr_next; + } + + return (head == matchP); + } + return false; +} + +/* Check if a frag is variable due to explicit relocations. */ + +static bool +reloc_variable_frag_p (fragS *fragp, asection *sec) +{ + struct reloc_list *rp; + /* Trivial for frags that are already marked. */ + if (fragp->tc_frag_data) + return true; + + rp = reloc_list; + + /* Now look for fixups in the explicit relocation list. + Update tc_frag_data.link_var here and now so the iteration doesn't + have to be repeated, at least for this frag. */ + if (rp != NULL + && symbol_get_frag (rp->u.a.offset_sym) == fragp) + { + fragp->tc_frag_data = 1; + return true; + } + return false; +} + +/* Compute the length of a branch, and adjust the RELAX_MD_TOOFAR16 + bit accordingly. */ + +static int +relaxed_16bit_branch_length (fragS *fragp, asection *sec, int update) +{ + bool toofar; + + if (fragp + && fragp->fr_symbol + && S_IS_DEFINED (fragp->fr_symbol) + && !S_IS_WEAK (fragp->fr_symbol) + && sec == S_GET_SEGMENT (fragp->fr_symbol)) + { + addressT addr; + offsetT val; + int type; + + val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset; + + /* Assume this is a 2-byte branch. */ + addr = fragp->fr_address + fragp->fr_fix + 2; + + val -= addr; + + type = RELAX_MD_TYPE (fragp->fr_subtype); + if (type == RT_BRANCH_UCND || type == RT_BALC_STUB) + toofar = val < -(0x200 << 1) || val >= (0x200 << 1); + else if (type == RT_BRANCH_CNDZ) + toofar = val < -(0x40 << 1) || val >= (0x40 << 1); + else if (type == RT_BRANCH_CND) + toofar = (val <= 0 + || val > 30 + || (val == 2 + && RELAX_MD_TOOFAR16 (fragp->fr_subtype) + && nanomips_frag_match (fragp, symbol_get_frag + (fragp->fr_symbol)))); + else + abort (); + } + else + /* If the symbol is not defined or it's in a different segment, + we emit a normal 32-bit branch. */ + toofar = true; + + if (fragp && update + && toofar != RELAX_MD_TOOFAR16 (fragp->fr_subtype)) + fragp->fr_subtype + = (toofar + ? RELAX_MD_MARK_TOOFAR16 (fragp->fr_subtype) + : RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype)); + + if (toofar) + return 4; + + return 2; +} + +/* Compute the length of a call potentially going through a BALC stub + and adjust the RELAX_MD_USESTUB bit accordingly. */ + +static int +relaxed_stub_call_length (fragS *fragp, asection *sec, bool toofar16, + bool update) +{ + bool usestub = false; + bool keepstub = false; + addressT callsite; + struct balc_stub *stub = NULL; + + /* Assume this is a 2-byte branch. */ + callsite = fragp->fr_address + fragp->fr_fix; + + /* Find start of correct stub-group list, if in different section or + at end of current list. */ + if ((stubg_now->seg != sec) + || (stubg_now->prev != NULL + && stubg_now->prev->fragp != NULL + && callsite < stubg_now->prev->fragp->fr_address)) + stubg_now = str_hash_find (balc_stubgroup_table, sec->name); + + /* This is for ugly cases of hand-written assembly where the + order of .ent and .section directives is reversed. */ + if (stubg_now == NULL || stubg_now->fragp == NULL) + return (toofar16 ? 4 : 2); + + while (stubg_now->fragp->fr_address + stubg_now->fragp->fr_fix > 0 + && stubg_now->fragp->fr_address + stubg_now->fragp->fr_fix < callsite) + if (stubg_now->next != NULL && stubg_now->next->fragp != NULL) + stubg_now = stubg_now->next; + else + break; + + gas_assert (stubg_now->seg == sec); + + usestub = balc_find_stub_inrange (S_GET_NAME (fragp->fr_symbol), + callsite, stubg_now, &stub); + + if (usestub && stub->fragp != NULL) + keepstub = RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype); + + /* We need this to happen only once per call frag, but as early as + possible in the estimation phase, before actual relaxation. */ + if (usestub && !toofar16) + { + usestub = false; + if (RELAX_MD_ONCESTUB (fragp->fr_subtype)) + { + fragp->fr_subtype + = RELAX_MD_CLEAR_ONCESTUB (fragp->fr_subtype); + if (callsite == stub->first_call->callsite) + stublist_pop_call (stub); + else + stublist_trunc_calls (stub, callsite); + } + } + /* Only track calls that cannot fit within 16-bit instruction. */ + if (usestub && toofar16 + && usestub != RELAX_MD_ONCESTUB (fragp->fr_subtype)) + { + stublist_append_call (stub, callsite); + fragp->fr_subtype = RELAX_MD_MARK_ONCESTUB (fragp->fr_subtype); + } + + if (update && usestub != RELAX_MD_USESTUB (fragp->fr_subtype)) + fragp->fr_subtype + = ((usestub && keepstub) + ? RELAX_MD_MARK_USESTUB (fragp->fr_subtype) + : RELAX_MD_CLEAR_USESTUB (fragp->fr_subtype)); + + if ((usestub && keepstub) || !toofar16) + return 2; + else + return 4; +} + +/* Compute the length of a BALC stub and adjust the RELAX_MD_KEEPSTUB + bit accordingly. If a stub is not to be instantiated, its length is 0. */ +static int +relaxed_stub_length (fragS *fragp, asection *sec, bool update) +{ + bool keepstub = false; + addressT stubsite; + struct balc_stub *stub = NULL; + + /* Assume this is a 2-byte branch. */ + stubsite = fragp->fr_address + fragp->fr_fix; + + if (stubg_now->fragp) + { + /* Section change. */ + if (stubg_now->seg != sec || (stubg_now->fragp->fr_address > stubsite)) + stubg_now = str_hash_find (balc_stubgroup_table, sec->name); + + while (stubg_now->fragp->fr_address < stubsite) + if (stubg_now->next && stubg_now->next->fragp + && stubg_now->next->fragp->fr_address <= stubsite) + stubg_now = stubg_now->next; + else + break; + } + + gas_assert (stubg_now->seg == sec); + + /* Retain this stub only if it has sufficient associated + calls and cannot be merged with another elligible stub. */ + if (balc_in_stub_group (S_GET_NAME (fragp->fr_symbol), + stubsite, stubg_now, &stub) + && stub->numcalls > 0 + && !balc_merge_stub (S_GET_NAME (fragp->fr_symbol), + stubg_now, &stub) + && stub->numcalls >= 3) + keepstub = true; + + if (keepstub != RELAX_MD_KEEPSTUB (fragp->fr_subtype)) + { + if (update) + fragp->fr_subtype + = keepstub ? RELAX_MD_MARK_KEEPSTUB (fragp->fr_subtype) + : RELAX_MD_CLEAR_KEEPSTUB (fragp->fr_subtype); + + stubg_now->next_offset += (keepstub ? 4 : -4); + } + + if (keepstub) + return 4; + else + { + /* A stub that is unused right up to the update stage + can be summarily pruned from the table. */ + if (update && stub && stub->numcalls == 0) + stubtable_delete (stubg_now->stubtable, + S_GET_NAME (fragp->fr_symbol)); + return 0; + } +} + +static bool +nanomips_gpr3_reg_p (unsigned long reg) +{ + /* Check for membership in set {16, 17, 18, 19, 4, 5, 6, 7}. */ + return ((reg >> 2 == 1 || reg >> 2 == 4)); +} + +/* Compute the length of an ADDIU instruction. */ +static int +relaxed_addiu_length (fragS *fragp, bool update) +{ + char *buf; + unsigned long insn; + offsetT sval; + unsigned long rt, rs; + bool toofar16 = true; + bool negoff = false; + + sval = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset; + buf = fragp->fr_literal + fragp->fr_fix; + insn = read_compressed_insn (buf, 4); + + rt = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RT, NANOMIPSOP_SH_RT); + rs = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RS, NANOMIPSOP_SH_RS); + + if (rt == rs + && sval >= -8 + && sval <= 7 + && !RELAX_MD_FIXED (fragp->fr_subtype)) + { + toofar16 = false; + negoff = true; + } + else if (nanomips_gpr3_reg_p (rs) + && nanomips_gpr3_reg_p (rt) + && sval >= 0 + && sval <= 28 + && sval % 4 == 0 + && !RELAX_MD_FIXED (fragp->fr_subtype)) + toofar16 = false; + else if (nanomips_gpr3_reg_p (rt) + && rs == 0 + && (int)sval >= -1 + && (int)sval <= 126 + && !RELAX_MD_FIXED (fragp->fr_subtype)) + toofar16 = false; + else + negoff = (sval < 0); + + if (fragp && update + && toofar16 != RELAX_MD_TOOFAR16 (fragp->fr_subtype)) + fragp->fr_subtype + = (toofar16 + ? RELAX_MD_MARK_TOOFAR16 (fragp->fr_subtype) + : RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype)); + + if (fragp && update && negoff != RELAX_MD_NEGOFF (fragp->fr_subtype)) + fragp->fr_subtype + = negoff ? RELAX_MD_MARK_NEGOFF (fragp->fr_subtype) + : RELAX_MD_CLEAR_NEGOFF (fragp->fr_subtype); + + if (toofar16) + return 4; + else + return 2; +} + +/* Estimate the size of a frag before relaxing. We are not really relaxing + here. The estimated size is encoded in the subtype information. */ + +int +md_estimate_size_before_relax (fragS *fragp, asection *segtype) +{ + int change; + + if (RELAX_MD_P (fragp->fr_subtype)) + { + int length = 4; + + if (!nanomips_opts.no_balc_stubs + && stubg_now != NULL + && RELAX_MD_BALC_STUB_P (fragp->fr_subtype)) + length = relaxed_stub_length (fragp, segtype, false); + else if (RELAX_MD_ADDIU_P (fragp->fr_subtype)) + length = relaxed_addiu_length (fragp, false); + else if (RELAX_MD_TYPE (fragp->fr_subtype) != 0) + length = relaxed_16bit_branch_length (fragp, segtype, false); + + /* Try to relax 32-bit call through a stub. */ + if (!nanomips_opts.no_balc_stubs + && stubg_now != NULL + && RELAX_MD_TYPE (fragp->fr_subtype) == RT_BRANCH_UCND + && RELAX_MD_LINK (fragp->fr_subtype)) + length = relaxed_stub_call_length (fragp, segtype, length > 2, false); + + fragp->fr_var = length; + return length; + } + + if (nanomips_opts.pic == NO_PIC) + change = nopic_need_relax (fragp->fr_symbol, 0); + else if ((nanomips_opts.pic == SVR4_PIC) + || (nanomips_opts.pic == SVR4_LARGE_PIC)) + change = pic_need_relax (fragp->fr_symbol, segtype); + else + abort (); + + if (change) + { + fragp->fr_subtype |= RELAX_USE_SECOND; + return -RELAX_FIRST (fragp->fr_subtype); + } + else + return -RELAX_SECOND (fragp->fr_subtype); +} + +/* This is called to see whether a reloc against a defined symbol + should be converted into a reloc against a section. */ + +int +nanomips_fix_adjustable (fixS *fixp) +{ + asection *tsect; + + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + + if (fixp->fx_addsy == NULL) + return 1; + + /* PC relative relocations for need to be symbol rather than section + relative to allow linker relaxations to be performed later on. */ + if (pcrel_reloc_p (fixp->fx_r_type)) + return 0; + + /* Relocations to code sections need to be symbol rather than section + relative for nanoMIPS, to allow linker expansions and relaxations + without having to adjust addends. */ + tsect = S_GET_SEGMENT (fixp->fx_addsy); + if (tsect != NULL + && (tsect->flags & SEC_CODE) != 0 + && (linkrelax || symbol_used_p (fixp->fx_addsy))) + return 0; + + return 1; +} + +/* Translate internal representation of relocation info to BFD target + format. */ + +arelent ** +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + static arelent *retval[MAX_RELOC_EXPANSION + 1]; + arelent *reloc; + bfd_reloc_code_real_type code; + + memset (retval, 0, sizeof (retval)); + reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + if (fixp->fx_pcrel && fixp->fx_r_type == BFD_RELOC_32_PCREL) + /* At this point, fx_addnumber is "symbol offset - pcrel address". + Relocations want only the symbol offset. */ + reloc->addend = fixp->fx_addnumber + reloc->address; + else + reloc->addend = fixp->fx_addnumber; + + code = fixp->fx_r_type; + + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent %s relocation in this object file" + " format"), bfd_get_reloc_code_name (code)); + retval[0] = NULL; + } + + if (linkrelax && fixp->fx_subsy) + { + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + reloc->addend = 0; + retval[1] = reloc; + + reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc->howto = bfd_reloc_type_lookup (stdoutput, + BFD_RELOC_NANOMIPS_NEG); + } + else if (linkrelax_reloc_p (fixp->fx_r_type)) + { + /* For a place-holder relocation, we want any succeeding PC-relative + relocation at the same offset to be generated first, so that its + PC-relative address calculation is disassembled correctly. */ + fixS *fixp_iter = fixp; + + while (fixp_iter->fx_next != NULL) + { + /* Search ahead for PC-relative relocations at same offset. */ + fixp_iter = fixp_iter->fx_next; + if (!linkrelax_reloc_p (fixp_iter->fx_r_type) + && (fixp_iter->fx_frag->fr_address + fixp_iter->fx_where > + fixp->fx_frag->fr_address + fixp->fx_where)) + break; + + if (fixp_iter->fx_pcrel && !fixp_iter->fx_done) + { + /* Relocation found, generate it before the place-holder + reloc and mark it as done. */ + retval[1] = reloc; + reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp_iter->fx_addsy); + reloc->address = (fixp_iter->fx_frag->fr_address + + fixp_iter->fx_where); + reloc->addend = fixp_iter->fx_addnumber; + reloc->howto = bfd_reloc_type_lookup (stdoutput, + fixp_iter->fx_r_type); + + fixp_iter->fx_done = true; + break; + } + } + } + return retval; +} + +/* Relax a machine dependent frag. This returns the amount by which + the current size of the frag should change. */ + +int +nanomips_relax_frag (asection *sec, fragS *fragp, + long stretch ATTRIBUTE_UNUSED) +{ + if (RELAX_MD_P (fragp->fr_subtype)) + { + offsetT old_var = fragp->fr_var; + offsetT new_var = 4; + + if (!nanomips_opts.no_balc_stubs + && stubg_now != NULL + && RELAX_MD_BALC_STUB_P (fragp->fr_subtype)) + new_var = relaxed_stub_length (fragp, sec, true); + else if (RELAX_MD_ADDIU_P (fragp->fr_subtype)) + new_var = relaxed_addiu_length (fragp, true); + else if (RELAX_MD_TYPE (fragp->fr_subtype) != 0) + new_var = relaxed_16bit_branch_length (fragp, sec, true); + else + new_var = old_var; + + /* Try to relax 32-bit call through a stub. */ + if (!nanomips_opts.no_balc_stubs + && stubg_now != NULL + && RELAX_MD_TYPE (fragp->fr_subtype) == RT_BRANCH_UCND + && RELAX_MD_LINK (fragp->fr_subtype) + && RELAX_MD_TOOFAR16 (fragp->fr_subtype)) + new_var = relaxed_stub_call_length (fragp, sec, new_var > 2, true); + + fragp->fr_var = new_var; + return new_var - old_var; + } + return 0; +} + +/* Convert a machine dependent frag. */ + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) +{ + if (RELAX_MD_ADDIU_P (fragp->fr_subtype)) + { + char *buf = fragp->fr_literal + fragp->fr_fix; + unsigned long insn = read_compressed_insn (buf, 4); + offsetT sval; + unsigned rt, rs; + + rt = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RT, NANOMIPSOP_SH_RT); + rs = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RS, NANOMIPSOP_SH_RS); + sval = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset; + + if (!RELAX_MD_TOOFAR16 (fragp->fr_subtype)) + { + if (RELAX_MD_NEGOFF (fragp->fr_subtype)) + /* ADDIU[32] -> ADDIU[RS5] */ + insn = (0x9008 + | (rt << NANOMIPSOP_SH_MP) + | (sval & 0x8) << 1 + | (sval & 0x7)); + else if (rs == 0) + /* LI[32] -> LI[16] */ + insn = (0xd000 + | (rt << NANOMIPSOP_SH_MM) + | (sval & 0x7f)); + else + /* ADDIU[32] -> ADDIU[R2] */ + insn = (0x9000 + | (rt & 0x7) << NANOMIPSOP_SH_MD + | (rs & 0x7) << NANOMIPSOP_SH_MC + | (sval & 0x1f) >> 2); + } + else if (RELAX_MD_NEGOFF (fragp->fr_subtype)) + /* ADDIU[32] -> ADDIU[NEG] */ + insn = (insn & ~0xfff) | 0x80008000 | (-sval & 0xfff); + else + insn = (insn & ~0xffff) | (sval & 0xffff); + + buf = write_compressed_insn (buf, insn, fragp->fr_var); + fragp->fr_fix += fragp->fr_var; + gas_assert (buf == fragp->fr_literal + fragp->fr_fix); + return; + } + else if (RELAX_MD_P (fragp->fr_subtype)) + { + char *buf = fragp->fr_literal + fragp->fr_fix; + int type = RELAX_MD_TYPE (fragp->fr_subtype); + unsigned long insn; + expressionS exp; + fixS *fixp; + symbolS *stubsym = NULL; + const bfd_reloc_code_real_type rtype[] + = { + BFD_RELOC_NANOMIPS_10_PCREL_S1, + BFD_RELOC_NANOMIPS_7_PCREL_S1, + BFD_RELOC_NANOMIPS_4_PCREL_S1, + BFD_RELOC_NANOMIPS_25_PCREL_S1, + BFD_RELOC_NANOMIPS_14_PCREL_S1, + BFD_RELOC_NANOMIPS_14_PCREL_S1 + }; + + if (RELAX_MD_USESTUB (fragp->fr_subtype) + && ((stubg_now->seg != (asection *) asec) + || (stubg_now->prev + && stubg_now->prev->fragp + && (fragp->fr_address + fragp->fr_fix + < stubg_now->prev->fragp->fr_address)))) + stubg_now = str_hash_find (balc_stubgroup_table, asec->name); + + exp.X_op = O_symbol; + if (RELAX_MD_USESTUB (fragp->fr_subtype) + && balc_get_stub_for_symbol (S_GET_NAME (fragp->fr_symbol), + fragp->fr_address + fragp->fr_fix, + stubg_now, &stubsym)) + { + /* Resolve call through a stub symbol. */ + exp.X_add_symbol = stubsym; + exp.X_add_number = 0; + fragp->fr_subtype + = RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype); + } + else + { + exp.X_add_symbol = fragp->fr_symbol; + exp.X_add_number = fragp->fr_offset; + } + + fragp->fr_fix += fragp->fr_var; + + /* We generate a fixup instead of applying it right now, + because if there is linker relaxation, we're going to + need the relocations. */ + switch (type) + { + case RT_BRANCH_UCND: + case RT_BRANCH_CNDZ: + case RT_BRANCH_CND: + fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, true, + rtype[type - RT_BRANCH_UCND + + (RELAX_MD_TOOFAR16 + (fragp->fr_subtype) ? 3 : 0)]); + break; + case RT_BALC_STUB: + if (!RELAX_MD_KEEPSTUB (fragp->fr_subtype)) + return; + fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, true, + rtype[3]); + break; + default: + abort (); + } + + fixp->fx_file = fragp->fr_file; + fixp->fx_line = fragp->fr_line; + + /* These relocations can have an addend that won't fit in + 2 octets. */ + fixp->fx_no_overflow = 1; + + if (RELAX_MD_USESTUB (fragp->fr_subtype) && stubsym != NULL) + { + /* Relax 32-bit call to 16-bit call to stub. */ + buf = write_compressed_insn (buf, 0x3800, 2); + return; + } + + /* Nothing left to do for 16-bit branches that fit, + or for balc stubs */ + if (!RELAX_MD_TOOFAR16 (fragp->fr_subtype) || type == RT_BALC_STUB) + return; + + /* Relax 16-bit branches to 32-bit branches. */ + insn = read_compressed_insn (buf, 2); + + if ((insn & 0xfc00) == 0xd800) /* beqc[16]/bnec[16] */ + { + unsigned long rt = (insn & 0x0380) >> 7; + unsigned long rs = (insn & 0x0070) >> 4; + if (rt > rs) + insn = 0x88000000; /* beqc */ + else + insn = 0xa8000000; /* bnec */ + rt = nanomips_to_32_reg_d_map[rt]; + rs = nanomips_to_32_reg_d_map[rs]; + insn |= (rt << 21); + insn |= (rs << 16); + } + else if ((insn & 0xdc00) == 0x9800) /* beqzc[16]/bnezc[16] */ + { + unsigned long rt = (insn & 0x0380) >> 7; + rt = nanomips_to_32_reg_d_map[rt]; + /* beqc: 0x9800 -> 0x88000000 + bnec: 0xb800 -> 0xa8000000 */ + insn = 0x88000000 | (((insn & 0x2000) >> 13) << 29); + insn |= (rt << 21); + } + else if ((insn & 0xdc00) == 0x1800) /* bc[16]/balc[16] */ + /* bc: 0x1800 -> 0x28000000 + balc: 0x3800 -> 0x2a000000 */ + insn = 0x28000000 | (((insn & 0x2000) >> 13) << 25); + else + abort (); + + /* Nothing else to do, just write it out. */ + buf = write_compressed_insn (buf, insn, 4); + gas_assert (buf == fragp->fr_literal + fragp->fr_fix); + return; + } + { + relax_substateT subtype = fragp->fr_subtype; + bool second_longer = (subtype & RELAX_SECOND_LONGER) != 0; + bool use_second = (subtype & RELAX_USE_SECOND) != 0; + int first = RELAX_FIRST (subtype); + int second = RELAX_SECOND (subtype); + fixS *fixp = (fixS *) fragp->fr_opcode; + + /* Possibly emit a warning if we've chosen the longer option. */ + if (use_second == second_longer) + { + subtype &= ~RELAX_NOMACRO; + } + + /* Go through all the fixups for the first sequence. Disable them + (by marking them as done) if we're going to use the second + sequence instead. */ + while (fixp + && fixp->fx_frag == fragp + && fixp->fx_where < fragp->fr_fix - second) + { + if (subtype & RELAX_USE_SECOND) + fixp->fx_done = 1; + fixp = fixp->fx_next; + } + + /* Go through the fixups for the second sequence. Disable them if + we're going to use the first sequence, otherwise adjust their + addresses to account for the relaxation. */ + while (fixp && fixp->fx_frag == fragp) + { + if (subtype & RELAX_USE_SECOND) + fixp->fx_where -= first; + else + fixp->fx_done = 1; + fixp = fixp->fx_next; + } + + /* Now modify the frag contents. */ + if (subtype & RELAX_USE_SECOND) + { + char *start; + + start = fragp->fr_literal + fragp->fr_fix - first - second; + memmove (start, start + first, second); + fragp->fr_fix -= first; + } + else + fragp->fr_fix -= second; + } +} + +/* This function is called whenever a label is defined, including fake + labels instantiated off the dot special symbol. */ + +static void +nanomips_record_label (symbolS *sym) +{ + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l; + + if (free_insn_labels == NULL) + l = (struct insn_label_list *) xmalloc (sizeof *l); + else + { + l = free_insn_labels; + free_insn_labels = l->next; + } + + l->label = sym; + l->next = si->label_list; + si->label_list = l; +} + +/* This function is called as tc_frob_label() whenever a label is defined + and adds a DWARF-2 record we only want for true labels. */ + +void +nanomips_define_label (symbolS *sym) +{ + nanomips_record_label (sym); + dwarf2_emit_label (sym); +} + +/* Some special processing for a nanoMIPS ELF file. */ + +void +nanomips_elf_final_processing (void) +{ + /* Set the nanoMIPS ELF flag bits. */ + if (nanomips_opts.pic != NO_PIC) + elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_PIC; + + if (linkrelax == true) + elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_LINKRELAX; + + /* Set the nanoMIPS ELF ABI flags. */ + if (nanomips_abi == P32_ABI) + elf_elfheader (stdoutput)->e_flags |= E_NANOMIPS_ABI_P32; + else if (nanomips_abi == P64_ABI) + elf_elfheader (stdoutput)->e_flags |= E_NANOMIPS_ABI_P64; + + if (nanomips_32bitmode) + elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_32BITMODE; +} + +static procS cur_proc; +static int numprocs; + +/* Fill in an rs_align_code fragment. Unlike elsewhere we want to use + 32-bit NOPs here (if applicable). */ + +void +nanomips_handle_align (fragS *fragp) +{ + char *p; + int bytes, size, excess; + valueT opcode; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + + /* tc_frag_data points to the ALIGN relocation for this frag. + Update the size of the absolute alignment symbol to match + the actual padding inserted at this alignment point. */ + if (fragp->tc_frag_data != NULL) + { + fixS *fixp = fragp->tc_frag_data; + asymbol *sym; + + while (fixp != NULL && fixp->fx_frag == fragp) + { + if (fixp->fx_r_type == BFD_RELOC_NANOMIPS_ALIGN) + { + sym = symbol_get_bfdsym (fixp->fx_addsy); + elf_symbol (sym)->internal_elf_sym.st_size = bytes; + break; + } + fixp = fixp->fx_next; + } + } + + if (fragp->fr_type != rs_align_code) + return; + + p = fragp->fr_literal + fragp->fr_fix; + opcode = nanomips_nop32_insn.insn_opcode; + size = 4; + + excess = bytes % size; + + /* Handle the leading part if we're not inserting a whole number of + instructions, and make it the end of the fixed part of the frag. + Try to fit in a short NOP if applicable and possible, + and use zeroes otherwise. */ + gas_assert (excess < 4); + fragp->fr_fix += excess; + switch (excess) + { + case 3: + *p++ = '\0'; + /* Fall through. */ + case 2: + if (!nanomips_opts.insn32) + { + p = write_compressed_insn (p, nanomips_nop16_insn.insn_opcode, 2); + break; + } + *p++ = '\0'; + /* Fall through. */ + case 1: + *p++ = '\0'; + /* Fall through. */ + case 0: + break; + } + + write_compressed_insn (p, opcode, size); + fragp->fr_var = size; +} + +static long +get_number (void) +{ + int negative = 0; + long val = 0; + + if (*input_line_pointer == '-') + { + ++input_line_pointer; + negative = 1; + } + if (!ISDIGIT (*input_line_pointer)) + as_bad (_("expected simple number")); + if (input_line_pointer[0] == '0') + { + if (input_line_pointer[1] == 'x') + { + input_line_pointer += 2; + while (ISXDIGIT (*input_line_pointer)) + { + val <<= 4; + val |= hex_value (*input_line_pointer++); + } + return negative ? -val : val; + } + else + { + ++input_line_pointer; + while (ISDIGIT (*input_line_pointer)) + { + val <<= 3; + val |= *input_line_pointer++ - '0'; + } + return negative ? -val : val; + } + } + if (!ISDIGIT (*input_line_pointer)) + { + printf (_(" *input_line_pointer == '%c' 0x%02x\n"), + *input_line_pointer, *input_line_pointer); + as_warn (_("invalid number")); + return -1; + } + while (ISDIGIT (*input_line_pointer)) + { + val *= 10; + val += *input_line_pointer++ - '0'; + } + return negative ? -val : val; +} + +/* The .file directive; just like the usual .file directive, but there + is an initial number which is the ECOFF file index. In the non-ECOFF + case .file implies DWARF-2. */ + +static void +s_nanomips_file (int x ATTRIBUTE_UNUSED) +{ + static int first_file_directive = 0; + + if (ECOFF_DEBUGGING) + { + get_number (); + s_file (0); + } + else + { + char *filename; + + filename = dwarf2_directive_filename (); + + /* Versions of GCC up to 3.1 start files with a ".file" + directive even for stabs output. Make sure that this + ".file" is handled. */ + if (filename != NULL && !first_file_directive) + { + new_logical_line (filename, -1); + s_file_string (filename); + } + first_file_directive = 1; + } +} + +/* The .loc directive, implying DWARF-2. */ + +static void +s_nanomips_loc (int x ATTRIBUTE_UNUSED) +{ + if (!ECOFF_DEBUGGING) + dwarf2_directive_loc (0); +} + +/* The .linkrelax directive. */ + +static void +s_linkrelax (int x ATTRIBUTE_UNUSED) +{ + linkrelax = true; + nanomips_linkrelax_p = true; +} + +/* The .end directive. */ + +static void +s_nanomips_end (int x ATTRIBUTE_UNUSED) +{ + symbolS *p; + + if (!is_end_of_stmt (*input_line_pointer)) + { + p = get_symbol (); + demand_empty_rest_of_line (); + } + else + p = NULL; + + if ((bfd_section_flags (now_seg) & SEC_CODE) == 0) + as_warn (_(".end not in text section")); + + if (!cur_proc_ptr) + { + as_warn (_(".end directive without a preceding .ent directive")); + demand_empty_rest_of_line (); + return; + } + + if (p != NULL) + { + gas_assert (S_GET_NAME (p)); + if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym))) + as_warn (_(".end symbol does not match .ent symbol")); + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p)); + } + else + as_warn (_(".end directive missing or unknown symbol")); + + /* Create an expression to calculate the size of the function. */ + if (p && cur_proc_ptr) + { + OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p); + expressionS *exp = notes_alloc (sizeof (*exp)); + + if (!nanomips_opts.no_balc_stubs && stubg_now != NULL) + stubgroup_wane (); + + obj->size = exp; + exp->X_op = O_subtract; + exp->X_add_symbol = symbol_temp_new_now (); + exp->X_op_symbol = p; + exp->X_add_number = 0; + + cur_proc_ptr->func_end_sym = exp->X_add_symbol; + } + cur_proc_ptr = NULL; +} + +/* The .aent and .ent directives. */ + +static void +s_nanomips_ent (int aent) +{ + symbolS *symbolP; + + symbolP = get_symbol (); + if (*input_line_pointer == ',') + ++input_line_pointer; + SKIP_WHITESPACE (); + if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-') + get_number (); + + if ((bfd_section_flags (now_seg) & SEC_CODE) == 0) + as_warn (_(".ent or .aent not in text section")); + + if (!aent && cur_proc_ptr) + as_warn (_("missing .end")); + + if (!aent) + { + cur_proc_ptr = &cur_proc; + memset (cur_proc_ptr, '\0', sizeof (procS)); + + cur_proc_ptr->func_sym = symbolP; + + ++numprocs; + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP)); + } + + symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION; + + if (stub_funcless_mode && !nanomips_opts.no_balc_stubs && stubg_now != NULL) + { + stub_funcless_mode = false; + stubgroup_wane (); + } + + stubgroup_new (now_seg); + demand_empty_rest_of_line (); +} + +static void +s_nanomips_frame (int ignore ATTRIBUTE_UNUSED) +{ + if (ECOFF_DEBUGGING) + s_ignore (ignore); + else + { + long val; + + if (cur_proc_ptr == NULL) + { + as_warn (_(".frame outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->frame_reg = tc_get_register (1); + + SKIP_WHITESPACE (); + if (*input_line_pointer++ != ',' + || get_absolute_expression_and_terminator (&val) != ',') + { + as_warn (_("bad .frame directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->frame_offset = val; + cur_proc_ptr->pc_reg = tc_get_register (0); + + demand_empty_rest_of_line (); + } +} + +/* The .fmask and .mask directives. */ + +static void +s_nanomips_mask (int reg_type) +{ + if (ECOFF_DEBUGGING) + s_ignore (reg_type); + else + { + long mask, off; + + if (cur_proc_ptr == NULL) + { + as_warn (_(".mask/.fmask outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + if (get_absolute_expression_and_terminator (&mask) != ',') + { + as_warn (_("bad .mask/.fmask directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + off = get_absolute_expression (); + + if (reg_type == 'F') + { + cur_proc_ptr->fpreg_mask = mask; + cur_proc_ptr->fpreg_offset = off; + } + else + { + cur_proc_ptr->reg_mask = mask; + cur_proc_ptr->reg_offset = off; + } + + demand_empty_rest_of_line (); + } +} + +/* A table describing all the processors gas knows about. Names are + matched in the order listed. + + To ease comparison, please keep this table in the same order as + gcc's nanomips_cpu_info_table[]. */ +static const struct nanomips_cpu_info nanomips_cpu_info_table[] = { + /* Entries for generic ISAs */ + { "32r6", NANOMIPS_CPU_IS_ISA, + ASE_xNMS, ISA_NANOMIPS32R6, CPU_NANOMIPS32R6 }, + { "32r6s", NANOMIPS_CPU_IS_ISA, + 0, ISA_NANOMIPS32R6, CPU_NANOMIPS32R6 }, + { "64r6", NANOMIPS_CPU_IS_ISA, + ASE_xNMS, ISA_NANOMIPS64R6, CPU_NANOMIPS64R6 }, + + /* End marker */ + { NULL, 0, 0, 0, 0 } +}; + +/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied + CPU name. */ + +static bool +nanomips_matching_cpu_name_p (const char *canonical, const char *given) +{ + while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical)) + given++, canonical++; + + return (*given == 0 && *canonical == 0); +} + +/* Parse an option that takes the name of a processor as its argument. + OPTION is the name of the option and CPU_STRING is the argument. + Return the corresponding processor enumeration if the CPU_STRING is + recognized, otherwise report an error and return null. + + A similar function exists in GCC. */ + +static const struct nanomips_cpu_info * +nanomips_parse_cpu (const char *option, const char *cpu_string) +{ + const struct nanomips_cpu_info *p; + + if (strcasecmp (cpu_string, "from-abi") == 0) + { + if (ABI_NEEDS_32BIT_REGS (nanomips_abi)) + return nanomips_cpu_info_from_isa (ISA_NANOMIPS32R6, false); + + if (ABI_NEEDS_64BIT_REGS (nanomips_abi)) + return nanomips_cpu_info_from_isa (ISA_NANOMIPS64R6, false); + + return nanomips_cpu_info_from_isa (NANOMIPS_DEFAULT_64BIT + ? ISA_NANOMIPS64R6 + : ISA_NANOMIPS32R6, + false); + } + + /* 'default' has traditionally been a no-op. Probably not very useful. */ + if (strcasecmp (cpu_string, "default") == 0) + return 0; + + for (p = nanomips_cpu_info_table; p->name != 0; p++) + if (nanomips_matching_cpu_name_p (p->name, cpu_string)) + return p; + + if (option != NULL) + as_bad (_("bad value (%s) for %s"), cpu_string, option); + return 0; +} + +/* Return the canonical processor information for ISA (a member of the + ISA_NANOMIPS* enumeration). */ + +static const struct nanomips_cpu_info * +nanomips_cpu_info_from_isa (int isa, bool subset) +{ + int i; + + for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++) + if ((nanomips_cpu_info_table[i].flags & NANOMIPS_CPU_IS_ISA) + && isa == nanomips_cpu_info_table[i].isa) + { + if (subset && (nanomips_cpu_info_table[i].ase & ASE_xNMS) != 0) + i++; + + return (&nanomips_cpu_info_table[i]); + } + return NULL; +} + +static const struct nanomips_cpu_info * +nanomips_cpu_info_from_arch (int arch) +{ + int i; + for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++) + if (arch == nanomips_cpu_info_table[i].cpu) + return (&nanomips_cpu_info_table[i]); + + return NULL; +} + +static void +show (FILE *stream, const char *string, int *col_p, int *first_p) +{ + if (*first_p) + { + fprintf (stream, "%24s", ""); + *col_p = 24; + } + else + { + fprintf (stream, ", "); + *col_p += 2; + } + + if (*col_p + strlen (string) > 72) + { + fprintf (stream, "\n%24s", ""); + *col_p = 24; + } + + fprintf (stream, "%s", string); + *col_p += strlen (string); + + *first_p = 0; +} + +void +md_show_usage (FILE *stream) +{ + int column, first; + size_t i; + + fprintf (stream, _("\ +nanoMIPS options:\n\ +-EB generate big endian output\n\ +-EL generate little endian output\n\ +-g, -g2 do not remove unneeded NOPs or swap branches\n\ +-G NUM allow referencing objects up to NUM bytes\n\ + implicitly with the gp register [default 8]\n")); + fprintf (stream, _("\ +-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n")); + + first = 1; + + for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++) + show (stream, nanomips_cpu_info_table[i].name, &column, &first); + show (stream, "from-abi", &column, &first); + fputc ('\n', stream); + fprintf (stream, _("\ +-m32 create p32 ABI object file (default)\n\ +-m64 create p64 ABI object file\n")); + fprintf (stream, _("\ +-mcmodel=MMODEL Generate code for a specific memory model,\n\ + where MMODEL is one of: auto, medium, large\n\ +-m[no-]pic [dis]allow Position Independent Code generation\n\ +-m[no-]PIC [dis]allow large model Position Independent Code generation\n\ +-m[no-]pid [dis]allow Position Independent Data access\n\ +-m[no-]pcrel [dis]allow PC-relative address calculations\n")); + + fprintf (stream, _("\ +-minsn32 only generate 32-bit nanoMIPS instructions\n\ +-mno-insn32 generate all nanoMIPS instructions\n\ +-mhard-float allow floating-point instructions\n\ +-msoft-float do not allow floating-point instructions\n\ +-msingle-float only allow 32-bit floating-point operations\n\ +-mdouble-float allow 32-bit and 64-bit floating-point operations\n\ +--[no-]construct-floats [dis]allow floating point values to be constructed\n")); + + fprintf (stream, _("\ +-mmt generate MT instructions\n\ +-mno-mt do not generate MT instructions\n")); + fprintf (stream, _("\ +-mvirt generate Virtualization instructions\n\ +-mno-virt do not generate Virtualization instructions\n")); + fprintf (stream, _("\ +-m[no-]balc-stubs enable/disable out-of-range call optimization\n\ + through trampoline stubs\n\ +-m[no-]legacyregs [dis]allow mumerical register formats\n")); + fprintf (stream, _("\ +--linkrelax allow linker relaxations\n\ +--trap, --no-break trap exception on div by 0 and mult overflow\n\ +--break, --no-trap break exception on div by 0 and mult overflow\n")); + fputc ('\n', stream); +} + +int +nanomips_dwarf2_addr_size (void) +{ + if (HAVE_64BIT_OBJECTS) + return 8; + else + return 4; +} + +/* Standard calling conventions leave the CFA at SP on entry. */ +void +nanomips_cfi_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa_register (SP); +} + +int +tc_nanomips_regname_to_dw2regnum (char *regname) +{ + unsigned int regnum = -1; + unsigned int reg; + + if (reg_lookup (®name, RTYPE_GP | RTYPE_NUM, ®)) + regnum = reg; + + return regnum; +} + +void +nanomips_md_finish (void) +{ + int fpabi = Val_GNU_NANOMIPS_ABI_FP_ANY; + + nanomips_flush_pending_output (); + if (cur_proc_ptr) + { + as_warn (_("missing .end at end of assembly")); + if (stubg_now != NULL) + stubgroup_wane (); + } + + if (stub_funcless_mode && !nanomips_opts.no_balc_stubs && stubg_now != NULL) + { + stub_funcless_mode = false; + stubgroup_wane (); + } + + /* Just in case no code was emitted, do the consistency check. */ + file_check_options (); +} + +/* Map a data slot size to its nanoMIPS relocation. */ +static bfd_reloc_code_real_type +nanomips_bytes_to_reloc (int nbytes, bool signed_val) +{ + bfd_reloc_code_real_type rel = BFD_RELOC_NONE; + switch (nbytes) + { + case 1: + if (signed_val) + rel = BFD_RELOC_NANOMIPS_SIGNED_8; + else + rel = BFD_RELOC_NANOMIPS_UNSIGNED_8; + break; + case 2: + if (signed_val) + rel = BFD_RELOC_NANOMIPS_SIGNED_16; + else + rel = BFD_RELOC_NANOMIPS_UNSIGNED_16; + break; + case 4: + rel = BFD_RELOC_32; + break; + case 8: + rel = BFD_RELOC_64; + break; + case 16: + break; + default: + as_bad (_("unsupported BFD relocation size %u"), nbytes); + rel = BFD_RELOC_NONE; + } + return rel; +} + +/* Check if the expression is a simple difference of local labels in + code sections. */ + +static bool +simple_diff_expr_p (expressionS *exp) +{ + return (exp->X_op == O_subtract + && exp->X_add_symbol + && exp->X_op_symbol + && exp->X_add_number == 0 + && symbol_constant_p (exp->X_add_symbol) + && symbol_constant_p (exp->X_op_symbol)); +} + +/* Parse a .byte, .word, etc. expression. + Values for the status register are specified with %st(label). + `label' will be right shifted by 2. */ + +bfd_reloc_code_real_type +nanomips_parse_cons_expression (expressionS *exp, unsigned int nbytes) +{ + bfd_reloc_code_real_type rel = BFD_RELOC_NONE; + int repeat = 1; + expression (exp); + + if (*input_line_pointer == ':') + { + expressionS count; + ++input_line_pointer; + expression (&count); + if (count.X_op != O_constant || count.X_add_number <= 0) + { + as_warn (_("unresolvable or nonpositive repeat count; using 1")); + return BFD_RELOC_NONE; + } + + /* The cons function is going to output this expression once. So we + output it count - 1 times. */ + repeat = count.X_add_number; + } + + /* Sometimes subtract expressions containing data labels are deemed + not-constant by general parse code because they fall in distinct + frags. These wouldn't be affected by linker relaxation which only + touches code, so we skip relocations for those expressions and hope + that this will not break any valid case. */ + if (linkrelax + && exp->X_op != O_constant + && (exp->X_op != O_subtract + || ((S_GET_SEGMENT (exp->X_add_symbol))->flags & SEC_CODE) != 0 + || ((S_GET_SEGMENT (exp->X_op_symbol))->flags & SEC_CODE) != 0)) + { + expressionS *iter; + bool done = false; + + /* By-pass explicit reloc generation for simple label differences to + allow the general framework to evaluate whether the difference is + fixed or link-time variable. */ + if (simple_diff_expr_p (exp)) + return rel; + + rel = nanomips_bytes_to_reloc (nbytes, sign_cons); + + /* Check if we can actually handle this expression. */ + iter = exp; + while (iter->X_add_symbol != NULL && !done) + { + switch (iter->X_op) + { + case O_subtract: + /* exp := symbol - exp */ + if (!symbol_constant_p (iter->X_add_symbol)) + { + as_bad ("Expression too complex to relocate!"); + rel = BFD_RELOC_NONE; + } + iter = symbol_get_value_expression (iter->X_op_symbol); + break; + + case O_right_shift: + /* exp := exp >> const */ + if (!symbol_constant_p (iter->X_op_symbol) + || S_GET_SEGMENT (iter->X_op_symbol) != absolute_section + || iter->X_add_number != 0) + { + as_bad ("Expression too complex to relocate!"); + rel = BFD_RELOC_NONE; + } + /* Fall through. */ + + case O_symbol: + /* exp := symbol [ + offset ] */ + iter = symbol_get_value_expression (iter->X_add_symbol); + break; + + default: + /* If the expression contains even one operator that we can't + map to relocations, then don't bother. */ + rel = BFD_RELOC_NONE; + done = true; + break; + } + } + } + while (repeat-- > 1) + emit_expr_with_reloc (exp, nbytes, rel); + + return rel; +} + +void +nanomips_cons_fix_new (fragS *frag, int where, int nbytes, expressionS *exp, + bfd_reloc_code_real_type r) +{ + if (r != BFD_RELOC_NONE) + { + /* Assuming 64 entries will be enough. */ + struct s_rentry + { + symbolS *sym; + offsetT addend; + bfd_reloc_code_real_type reloc; + } rentry[64]; + size_t numrelocs = 0; + bool comp_p; + bfd_reloc_code_real_type sym_reloc = (HAVE_64BIT_SYMBOLS + ? BFD_RELOC_64 : BFD_RELOC_32); + expressionS *iter = exp; + + /* Provisionally fill the outer-most entry. */ + rentry[numrelocs].sym = NULL; + rentry[numrelocs].addend = 0; + rentry[numrelocs].reloc = r; + + /* Dealing with composite relocation. */ + comp_p = (iter->X_op != O_symbol); + + if (iter->X_op == O_right_shift && r != sym_reloc) + /* Finalize the outer entry now. */ + numrelocs++; + else + /* Defer the outer entry for further combination. */ + sym_reloc = r; + + while (iter->X_add_symbol != NULL) + { + + if (numrelocs + 1 >= ARRAY_SIZE (rentry)) + { + as_bad ("Expression too complex to relocate!"); + break; + } + + switch (iter->X_op) + { + case O_subtract: + rentry[numrelocs].sym = iter->X_add_symbol; + rentry[numrelocs].addend = 0; + rentry[numrelocs++].reloc = sym_reloc; + rentry[numrelocs].sym = iter->X_op_symbol; + rentry[numrelocs].addend = iter->X_add_number; + sym_reloc = BFD_RELOC_NANOMIPS_NEG; + break; + + case O_right_shift: + { + int scount; + gas_assert (symbol_constant_p (iter->X_op_symbol)); + scount = S_GET_VALUE (iter->X_op_symbol); + + /* Iterate over the shift count and track the + required number of relocs. */ + while (scount-- > 0) + { + rentry[numrelocs].sym = NULL; + rentry[numrelocs].addend = 0; + rentry[numrelocs++].reloc = BFD_RELOC_NANOMIPS_ASHIFTR_1; + } + + /* We can roll the a shift step in to the expression + if it is just a simple symbol reference. This just + sets things up for the last-level block below. */ + if (S_GET_VALUE (iter->X_op_symbol) != 0 + && sym_reloc != BFD_RELOC_NANOMIPS_NEG) + { + sym_reloc = BFD_RELOC_NANOMIPS_ASHIFTR_1; + numrelocs--; + } + } + /* Fall through */ + + case O_symbol: + rentry[numrelocs].sym = iter->X_add_symbol; + if (sym_reloc == BFD_RELOC_NANOMIPS_NEG) + /* S1 - (S2 + A) => S1 + (-S2 + -A) */ + rentry[numrelocs].addend = -iter->X_add_number; + else + rentry[numrelocs].addend = iter->X_add_number; + break; + + default: + as_bad ("Expression too complex to relocate!"); + break; + } + + iter = symbol_get_value_expression (rentry[numrelocs].sym); + + /* No more levels of nesting? */ + if (iter->X_add_symbol == NULL) + rentry[numrelocs++].reloc = sym_reloc; + } + + /* Step through the list in reverse and create relocations. */ + while (numrelocs > 0) + { + fixS *fixP; + --numrelocs; + fixP = fix_new (frag, where, nbytes, rentry[numrelocs].sym, + rentry[numrelocs].addend, 0, + rentry[numrelocs].reloc); + + if (comp_p) + fixP->fx_tcbit = 1; + } + } + else + { + reloc_howto_type *reloc_howto; + int size = nbytes; + + if (r != TC_PARSE_CONS_RETURN_NONE) + { + reloc_howto = bfd_reloc_type_lookup (stdoutput, r); + size = bfd_get_reloc_size (reloc_howto); + + if (size > nbytes) + { + as_bad (_("%s relocations do not fit in %u bytes\n"), + reloc_howto->name, nbytes); + return; + } + } + else + switch (size) + { + case 1: + r = BFD_RELOC_8; + break; + case 2: + r = BFD_RELOC_16; + break; + case 3: + r = BFD_RELOC_24; + break; + case 4: + r = BFD_RELOC_32; + break; + case 8: + r = BFD_RELOC_64; + break; + default: + as_bad (_("unsupported BFD relocation size %u"), size); + return; + } + + fix_new_exp (frag, where, size, exp, 0, r); + } +} + +/* Check if subtraction of 2 symbols can be fully evaluated at assembly. */ +static bool +nanomips_allow_local_subtract_symbols (symbolS *left, symbolS *right, + bool pre_relax) +{ + /* Check for intervening fixups between symbols within the same frag to + determine if relaxation could affect the difference. */ + if (symbol_get_frag (left) == symbol_get_frag (right)) + { + fixS *fixP; + fragS *fragP; + bfd_vma low, high; + struct reloc_list *rp; + + fragP = symbol_get_frag (left); + + high = S_GET_VALUE (left); + low = S_GET_VALUE (right); + + /* Swap if necessary. */ + if (high < low) + { + high = high + low; + low = high - low; + high = high - low; + } + + /* Start with the first fix-up in this frag. */ + fixP = fragP->tc_frag_data; + + /* Now look for fixups within the range. */ + while (fixP != NULL + && fixP->fx_where < low + && fixP->fx_where < high) + fixP = fixP->fx_next; + + /* Fixups within the range. */ + if (fixP != NULL && fixP->fx_where < high) + return false; + + /* Too early for checking explicit relocations, assume the worst. */ + if (pre_relax) + return false; + + rp = reloc_list; + while (rp != NULL) + { + if (rp->u.a.offset_sym) + { + if (S_GET_VALUE (rp->u.a.offset_sym) >= low + && S_GET_VALUE (rp->u.a.offset_sym) < high) + return false; + else if (S_GET_VALUE (rp->u.a.offset_sym) > high) + break; + } + rp = rp->next; + } + /* No fixup within the range. */ + return true; + } + + /* We have to assume that there may be instructions between the + two symbols and that relaxation may increase the distance between + them. */ + return false; +} + +/* md_allow_local_subtract interface + Check if a subtraction expression can be fully evaluated. */ +bool +nanomips_allow_local_subtract (expressionS *left, expressionS *right, + segT section) +{ + /* If the symbols are not in a code section then they are OK. */ + if ((section->flags & SEC_CODE) == 0) + return true; + + if ((left->X_add_symbol == right->X_add_symbol)) + return true; + + if (!linkrelax) + return true; + + /* For lable-difference relocations in .debug_info sections, we + want to force a relocation, even if the difference is link-time + invariant so that the linker has the opportunity to zero-out + AT_high_pc expressions when the corresponding sections get + garbage-collected. Spurious non-zero AT_high_pc fields of + garbage-collected sections can trip-up GDB source disassembly. */ + if (strcmp (now_seg->name, ".debug_info") == 0) + return false; + else + return nanomips_allow_local_subtract_symbols (left->X_add_symbol, + right->X_add_symbol, + true); +} + +/* Create relocations for alignment directives. */ +void +nanomips_md_do_align (int n, const char *fill, int fill_length, int max_fill) +{ + unsigned int fill_value = 0; + + if (nanomips_linkrelax_p + && (bfd_section_flags (now_seg) & SEC_CODE) != 0 + && n > 1) + { + fill_value = bfd_get_bits (fill, fill_length * 8, target_big_endian); + + if (fill == NULL && subseg_text_p (now_seg)) + frag_grow (MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max)); + else if (fill_length > 1) + frag_grow (fill_length); + else + frag_grow (1); + + create_align_relocs (frag_now, n, fill_value, fill_length, max_fill); + } +} + +/* TC_VALIDATE_FIX_SUB hook. */ + +bool +nanomips_validate_fix_sub (fixS *fix) +{ + segT add_symbol_segment, sub_symbol_segment; + + /* The difference of two symbols should be resolved by the assembler when + linkrelax is not set. If the linker may relax the section containing + the symbols, then composite relocations are generated so that the + linker knows how to adjust the difference value. */ + if (!linkrelax || fix->fx_addsy == NULL) + return 0; + + /* Make sure both symbols are in the same segment, and that segment is + "normal" and relaxable. If the segment is not "normal", then the + fix is not valid. If the segment is not "relaxable", then the fix + should have been handled earlier. */ + add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy); + if (!SEG_NORMAL (add_symbol_segment) + || (add_symbol_segment->flags & SEC_DEBUGGING) != 0) + return 0; + + sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy); + + if (sub_symbol_segment != add_symbol_segment) + return 0; + else + { + /* For lable-difference relocations in .debug_info sections, we + want to force a relocation, even if the difference is link-time + invariant so that the linker has the opportunity to zero-out + AT_high_pc expressions when the corresponding sections get + garbage-collected. Spurious non-zero AT_high_pc fields for + garbage-collected sections can trip-up GDB source disassembly. */ + if (frag_in_section_p (fix->fx_frag, ".debug_info")) + return 1; + else + return (!nanomips_allow_local_subtract_symbols (fix->fx_addsy, + fix->fx_subsy, + false)); + } +} + +/* TC_EH_FRAME_ESTIMATE_SIZE_BEFORE_RELAX hook. + Check if the rs_cfa frag could be affected by linker relaxation and + choose a suitable size estimation heuristic. If not, fall back to + the generic estimation logic. + + The heuristic is currently hard-coded to 4 bytes. */ +int +nanomips_eh_frame_estimate_size_before_relax (fragS *frag) +{ + expressionS *exp = symbol_get_value_expression (frag->fr_symbol); + + if (linkrelax + && !nanomips_allow_local_subtract_symbols (exp->X_add_symbol, + exp->X_op_symbol, + false)) + { + int ret; + gas_assert ((frag->fr_subtype >> 3) > 0); + ret = 4; + frag->fr_subtype = (frag->fr_subtype & ~7) | ret; + + return ret; + } + else + return eh_frame_estimate_size_before_relax (frag); +} + +/* TC_EH_FRAME_RELAX_FRAG hook */ +int +nanomips_eh_frame_relax_frag (fragS *frag) +{ + int oldsize, newsize; + + oldsize = frag->fr_subtype & 7; + newsize = nanomips_eh_frame_estimate_size_before_relax (frag); + return newsize - oldsize; +} + +/* TC_EH_FRAME_CONVERT_FRAG hook + Check if the rs_cfa frag could be affected by linker relaxation and + generate relocations to fix-up the value after relaxation. + Then fall-back to the generic convert_frag. */ +void +nanomips_eh_frame_convert_frag (fragS *frag) +{ + expressionS *exp = symbol_get_value_expression (frag->fr_symbol); + + if (linkrelax && !nanomips_allow_local_subtract_symbols (exp->X_add_symbol, + exp->X_op_symbol, + false)) + { + /* Generate label-difference relocations for this frag. */ + int size = nanomips_eh_frame_estimate_size_before_relax (frag); + bfd_reloc_code_real_type r = nanomips_bytes_to_reloc (size, false); + + nanomips_cons_fix_new (frag, frag->fr_fix, size, exp, r); + } + eh_frame_convert_frag (frag); +} + +/* Hook to run before relaxation. */ + +void nanomips_pre_relax_hook (void) +{ + struct reloc_list *iter, *prev, *copy, *next; + + /* Make a private copy of the reloc_list and reverse it, + all in one iteration. */ + iter = reloc_list; + prev = copy = NULL; + + while (iter != NULL) + { + if (iter->u.a.howto->type != R_NANOMIPS_FRAME_REG) + { + reloc_list_copy = XNEW (struct reloc_list); + *reloc_list_copy = *iter; + reloc_list_copy->next = prev; + prev = reloc_list_copy; + } + iter = iter->next; + } + + /* Once reversed, reloc_list is mostly sorted, on account of how + the compiler emits explicit relocations. Optimized lookup + depends on the order of .relocs in the source matching the + order of offset symbols for those relocations. We either confirm + that the order matches or fall-back to a slower lookup. */ + iter = reloc_list_copy; + if (iter != NULL) + next = iter->next; + + while (iter != NULL && next != NULL) + { + fragS *fiter = symbol_get_frag (iter->u.a.offset_sym); + fragS *fnext = symbol_get_frag (next->u.a.offset_sym); + + if (fiter == fnext) + { + if (S_GET_VALUE (iter->u.a.offset_sym) + > S_GET_VALUE (next->u.a.offset_sym)) + { + reloc_ordered = false; + break; + } + } + else + { + while (fiter && fiter != fnext) + fiter = fiter->fr_next; + if (fiter != fnext) + { + reloc_ordered = false; + break; + } + } + iter = next; + next = iter->next; + } +} + +/* Hook to run after relaxation. */ + +void nanomips_post_relax_hook (void) +{ + reloc_list_iter = reloc_list_copy; +} diff --git a/gas/config/tc-nanomips.h b/gas/config/tc-nanomips.h new file mode 100644 index 00000000000..518bf8a8818 --- /dev/null +++ b/gas/config/tc-nanomips.h @@ -0,0 +1,187 @@ +/* tc-nanomips.h -- header file for tc-nanomips.c. + Copyright (C) 2017-2026 Free Software Foundation, CIP United Inc. + Contributed by MIPS Tech LLC. + Written by Faraz Shahbazker <[email protected]> + + 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. */ + +#ifndef TC_NANOMIPS +#define TC_NANOMIPS + +struct frag; +struct expressionS; + +/* Default to little endian. */ +#ifndef TARGET_BYTES_BIG_ENDIAN +#define TARGET_BYTES_BIG_ENDIAN 0 +#endif + +#define TARGET_ARCH bfd_arch_nanomips + +#define WORKING_DOT_WORD 1 +#define OLD_FLOAT_READS +#define RELOC_EXPANSION_POSSIBLE +#define MAX_RELOC_EXPANSION 2 +#define LOCAL_LABELS_FB 1 + +#define TC_ADDRESS_BYTES nanomips_address_bytes +extern int nanomips_address_bytes (void); + +#define md_relax_frag(segment, fragp, stretch) \ + nanomips_relax_frag(segment, fragp, stretch) +extern int nanomips_relax_frag (asection *, struct frag *, long); + +#define md_undefined_symbol(name) (0) +#define md_operand(x) + +extern void nanomips_handle_align (struct frag *); +#define HANDLE_ALIGN(sec, fragp) nanomips_handle_align (fragp) + +#define MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max) (3 + 4) + +struct insn_label_list; +#define TC_SEGMENT_INFO_TYPE struct insn_label_list * + +/* The endianness of the target format may change based on command + line arguments. */ +#define TARGET_FORMAT nanomips_target_format() +extern const char *nanomips_target_format (void); + +extern int tc_get_register (int); + +#define md_after_parse_args() nanomips_after_parse_args() +extern void nanomips_after_parse_args (void); + +#define tc_frob_label(sym) nanomips_define_label (sym) +extern void nanomips_define_label (symbolS *); + +#define tc_frob_file_before_adjust() nanomips_frob_file_before_adjust () +extern void nanomips_frob_file_before_adjust (void); + +#define tc_fix_adjustable(fixp) nanomips_fix_adjustable (fixp) +extern int nanomips_fix_adjustable (struct fix *); + +/* Values passed to md_apply_fix don't include symbol values. */ +#define MD_APPLY_SYM_VALUE(FIX) 0 + +#define TC_FORCE_RELOCATION(FIX) nanomips_force_relocation (FIX) +extern int nanomips_force_relocation (struct fix *); + +#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \ + (! SEG_NORMAL (SEG) || nanomips_force_relocation (FIX)) + +#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \ + (((SEG)->flags & SEC_CODE) != 0 && nanomips_force_relocation (FIX)) + +#define elf_tc_final_processing nanomips_elf_final_processing +extern void nanomips_elf_final_processing (void); + +extern void nanomips_md_finish (void); +#define md_finish() nanomips_md_finish() + +extern void nanomips_pop_insert (void); +#define md_pop_insert() nanomips_pop_insert() + +extern void nanomips_flush_pending_output (void); +#define md_flush_pending_output nanomips_flush_pending_output + +extern void nanomips_enable_auto_align (void); +#define md_elf_section_change_hook() nanomips_enable_auto_align() + +extern int nanomips_dwarf2_addr_size (void); +#define DWARF2_ADDR_SIZE(bfd) nanomips_dwarf2_addr_size () +#define DWARF2_FDE_RELOC_SIZE nanomips_dwarf2_addr_size () + +#define TARGET_USE_CFIPOP 1 + +#define tc_cfi_frame_initial_instructions nanomips_cfi_frame_initial_instructions +extern void nanomips_cfi_frame_initial_instructions (void); + +#define tc_regname_to_dw2regnum tc_nanomips_regname_to_dw2regnum +extern int tc_nanomips_regname_to_dw2regnum (char *regname); + +#define DWARF2_DEFAULT_RETURN_COLUMN 31 +#define DWARF2_CIE_DATA_ALIGNMENT (-4) + +/* Definitions needed to parse cons in target-specific manner. */ +#define TC_PARSE_CONS_RETURN_TYPE bfd_reloc_code_real_type +#define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE + +/* Parse a cons expression to determined what kind of relocations + may be needed to represent it. */ +extern bfd_reloc_code_real_type +nanomips_parse_cons_expression (struct expressionS *, unsigned); +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \ + nanomips_parse_cons_expression (EXP, NBYTES) + +/* Walk a cons expression tree and create the relocations needed + to represent the operation. */ +extern void nanomips_cons_fix_new (struct frag *, int, int, + struct expressionS *, + const bfd_reloc_code_real_type); +#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \ + nanomips_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC) + +/* Determine what kind of difference expressions must be + evaluated by assembler. */ +extern bool nanomips_allow_local_subtract (expressionS *, + expressionS *, segT); +#define md_allow_local_subtract(lhs,rhs,sect) \ + nanomips_allow_local_subtract (lhs, rhs, sect) + +#define TC_FRAG_TYPE struct fix * + +extern void nanomips_md_do_align (int, const char *, int, int); +#define md_do_align(n,f,l,m,j) nanomips_md_do_align (n,f,l,m) + +/* If defined, this macro allows control over whether fixups for a + given section will be processed when the linkrelax variable is + set. Define it to zero and handle things in md_apply_fix instead.*/ +#define TC_LINKRELAX_FIXUP(SEG) 0 + +/* This macro is evaluated for any fixup with a fx_subsy that + fixup_segment cannot reduce to a number. If the macro returns + false an error will be reported. */ +#define TC_VALIDATE_FIX_SUB(fix, seg) nanomips_validate_fix_sub (fix) +extern bool nanomips_validate_fix_sub (struct fix *); + +#define LEX_BR LEX_NAME + +#define MD_PCREL_FROM_SECTION(FIX,SEG) 0 + +int nanomips_eh_frame_estimate_size_before_relax (fragS *); +#define TC_EH_FRAME_ESTIMATE_SIZE_BEFORE_RELAX(frag) \ + nanomips_eh_frame_estimate_size_before_relax (frag) + +int nanomips_eh_frame_relax_frag (fragS *); +#define TC_EH_FRAME_RELAX_FRAG(frag) \ + nanomips_eh_frame_relax_frag (frag) + +void nanomips_eh_frame_convert_frag (fragS *); +#define TC_EH_FRAME_CONVERT_FRAG(frag) \ + nanomips_eh_frame_convert_frag (frag) + +#define md_section_align(SEGMENT, SIZE) (SIZE) + +void nanomips_pre_relax_hook (void); +#define md_pre_relax_hook nanomips_pre_relax_hook() + +void nanomips_post_relax_hook (void); +#define md_post_relax_hook nanomips_post_relax_hook() + +#endif /* TC_NANOMIPS */ diff --git a/gas/configure b/gas/configure index 3b120d3f441..b8d25684409 100755 --- a/gas/configure +++ b/gas/configure @@ -12694,6 +12694,60 @@ _ACEOF cat >>confdefs.h <<_ACEOF #define MIPS_DEFAULT_ABI $mips_default_abi +_ACEOF + + ;; + nanomips) + # Set nanomips_cpu to the name of the default CPU. + case ${target_cpu} in + nanomips | nanomipsel | nanomipseb) + nanomips_cpu=from-abi + ;; + nanomips32*) + nanomips_cpu=32r6 + ;; + nanomips64*) + nanomips_cpu=64r6 + ;; + *) + as_fn_error "$target_cpu isn't a supported NANOMIPS CPU name" "$LINENO" 5 + ;; + esac + # Decide whether to generate 32-bit or 64-bit code by default. + # Used to resolve -march=from-abi when an embedded ABI is selected. + case ${target} in + nanomips64*-*-*) + nanomips_default_64bit=1 + ;; + *) + nanomips_default_64bit=0 + ;; + esac + # Decide which ABI to target by default. + case ${target} in + nanomips64*-*) + nanomips_default_abi=P64_ABI + ;; + nanomips*-*) + nanomips_default_abi=P32_ABI + ;; + *) + nanomips_default_abi=NO_ABI + ;; + esac + +cat >>confdefs.h <<_ACEOF +#define NANOMIPS_CPU_STRING_DEFAULT "$nanomips_cpu" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NANOMIPS_DEFAULT_64BIT $nanomips_default_64bit +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NANOMIPS_DEFAULT_ABI $nanomips_default_abi _ACEOF ;; diff --git a/gas/configure.ac b/gas/configure.ac index 69547f5ffb9..03c0cb17904 100644 --- a/gas/configure.ac +++ b/gas/configure.ac @@ -423,6 +423,51 @@ changequote([,])dnl AC_DEFINE_UNQUOTED(MIPS_DEFAULT_ABI, $mips_default_abi, [Choose a default ABI for MIPS targets. ]) ;; + nanomips) + # Set nanomips_cpu to the name of the default CPU. + case ${target_cpu} in + nanomips | nanomipsel | nanomipseb) + nanomips_cpu=from-abi + ;; + nanomips32*) + nanomips_cpu=32r6 + ;; + nanomips64*) + nanomips_cpu=64r6 + ;; + *) + AC_MSG_ERROR($target_cpu isn't a supported MIPS CPU name) + ;; + esac + # Decide whether to generate 32-bit or 64-bit code by default. + # Used to resolve -march=from-abi when an embedded ABI is selected. + case ${target} in + nanomips64*-*-*) + nanomips_default_64bit=1 + ;; + *) + nanomips_default_64bit=0 + ;; + esac + # Decide which ABI to target by default. + case ${target} in + nanomips64*-*) + nanomips_default_abi=P64_ABI + ;; + nanomips*-*) + nanomips_default_abi=P32_ABI + ;; + *) + nanomips_default_abi=NO_ABI + ;; + esac + AC_DEFINE_UNQUOTED(NANOMIPS_CPU_STRING_DEFAULT, "$nanomips_cpu", + [Default CPU for nanoMIPS targets. ]) + AC_DEFINE_UNQUOTED(NANOMIPS_DEFAULT_64BIT, $nanomips_default_64bit, + [Generate 64-bit code by default on nanoMIPS targets. ]) + AC_DEFINE_UNQUOTED(NANOMIPS_DEFAULT_ABI, $nanomips_default_abi, + [Choose a default ABI for nanoMIPS targets. ]) + ;; esac # Do we need the opcodes library? diff --git a/gas/configure.tgt b/gas/configure.tgt index 7a5645fd9ae..8e1e7727603 100644 --- a/gas/configure.tgt +++ b/gas/configure.tgt @@ -85,6 +85,8 @@ case ${cpu} in mips*el) cpu_type=mips endian=little ;; mips*) cpu_type=mips endian=big ;; mt) cpu_type=mt endian=big ;; + nanomips*eb) cpu_type=nanomips endian=big ;; + nanomips*) cpu_type=nanomips endian=little ;; nds32be) cpu_type=nds32 endian=big ;; nds32le) cpu_type=nds32 endian=little ;; or1k* | or1knd*) cpu_type=or1k endian=big ;; @@ -345,6 +347,9 @@ case ${generic_target} in msp430-*-*) fmt=elf ;; + nanomips*-*-elf*) fmt=elf ;; + nanomips*-*-linux*) fmt=elf em=linux;; + nds32-*-elf*) fmt=elf ;; nds32-*-linux*) fmt=elf em=linux ;; diff --git a/gas/read.c b/gas/read.c index ae481007c32..a25828839d2 100644 --- a/gas/read.c +++ b/gas/read.c @@ -2615,7 +2615,7 @@ bss_alloc (symbolS *symbolP, addressT size, unsigned int align) subsegT current_subseg = now_subseg; segT bss_seg = bss_section; -#if defined (TC_MIPS) || defined (TC_ALPHA) +#if defined (TC_MIPS) || defined (TC_ALPHA) || defined (TC_NANOMIPS) if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour || OUTPUT_FLAVOR == bfd_target_elf_flavour) { diff --git a/ld/configure.tgt b/ld/configure.tgt index 60b62e69c08..644b6631a90 100644 --- a/ld/configure.tgt +++ b/ld/configure.tgt @@ -545,6 +545,8 @@ mips*-sde-elf* | mips*-mti-elf* | mips*-img-elf*) targ_emul=elf32btsmip targ_extra_emuls="elf32ltsmip elf32btsmipn32 elf64btsmip elf32ltsmipn32 elf64ltsmip" ;; +nanomips*-*) targ_emul=vanilla + ;; mips64*el-ps2-elf*) targ_emul=elf32lr5900n32 targ_extra_libpath=elf32lr5900 ;; -- 2.39.5