| /* tc-mips.c -- assemble code for a MIPS chip. |
| Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 |
| Free Software Foundation, Inc. |
| Contributed by the OSF and Ralph Campbell. |
| Written by Keith Knowles and Ralph Campbell, working independently. |
| Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus |
| Support. |
| |
| This file is part of GAS. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, 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, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| #include "as.h" |
| #include "config.h" |
| #include "subsegs.h" |
| |
| #include <ctype.h> |
| |
| #ifdef USE_STDARG |
| #include <stdarg.h> |
| #endif |
| #ifdef USE_VARARGS |
| #include <varargs.h> |
| #endif |
| |
| #include "opcode/mips.h" |
| #include "itbl-ops.h" |
| |
| #ifdef DEBUG |
| #define DBG(x) printf x |
| #else |
| #define DBG(x) |
| #endif |
| |
| #ifdef OBJ_MAYBE_ELF |
| /* Clean up namespace so we can include obj-elf.h too. */ |
| static int mips_output_flavor PARAMS ((void)); |
| static int mips_output_flavor () { return OUTPUT_FLAVOR; } |
| #undef OBJ_PROCESS_STAB |
| #undef OUTPUT_FLAVOR |
| #undef S_GET_ALIGN |
| #undef S_GET_SIZE |
| #undef S_SET_ALIGN |
| #undef S_SET_SIZE |
| #undef obj_frob_file |
| #undef obj_frob_file_after_relocs |
| #undef obj_frob_symbol |
| #undef obj_pop_insert |
| #undef obj_sec_sym_ok_for_reloc |
| #undef OBJ_COPY_SYMBOL_ATTRIBUTES |
| |
| #include "obj-elf.h" |
| /* Fix any of them that we actually care about. */ |
| #undef OUTPUT_FLAVOR |
| #define OUTPUT_FLAVOR mips_output_flavor() |
| #endif |
| |
| #if defined (OBJ_ELF) |
| #include "elf/mips.h" |
| #endif |
| |
| #ifndef ECOFF_DEBUGGING |
| #define NO_ECOFF_DEBUGGING |
| #define ECOFF_DEBUGGING 0 |
| #endif |
| |
| #include "ecoff.h" |
| |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| static char *mips_regmask_frag; |
| #endif |
| |
| #define AT 1 |
| #define TREG 24 |
| #define PIC_CALL_REG 25 |
| #define KT0 26 |
| #define KT1 27 |
| #define GP 28 |
| #define SP 29 |
| #define FP 30 |
| #define RA 31 |
| |
| #define ILLEGAL_REG (32) |
| |
| /* Allow override of standard little-endian ECOFF format. */ |
| |
| #ifndef ECOFF_LITTLE_FORMAT |
| #define ECOFF_LITTLE_FORMAT "ecoff-littlemips" |
| #endif |
| |
| extern int target_big_endian; |
| |
| /* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the |
| 32 bit ABI. This has no meaning for ECOFF. |
| Note that the default is always 32 bit, even if "configured" for |
| 64 bit [e.g. --target=mips64-elf]. */ |
| static int mips_64; |
| |
| /* The default target format to use. */ |
| |
| const char * |
| mips_target_format () |
| { |
| switch (OUTPUT_FLAVOR) |
| { |
| case bfd_target_aout_flavour: |
| return target_big_endian ? "a.out-mips-big" : "a.out-mips-little"; |
| case bfd_target_ecoff_flavour: |
| return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT; |
| case bfd_target_coff_flavour: |
| return "pe-mips"; |
| case bfd_target_elf_flavour: |
| #ifdef TE_TMIPS |
| /* This is traditional mips */ |
| return (target_big_endian |
| ? (mips_64 ? "elf64-tradbigmips" : "elf32-tradbigmips") |
| : (mips_64 ? "elf64-tradlittlemips" : "elf32-tradlittlemips")); |
| #else |
| return (target_big_endian |
| ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips") |
| : (mips_64 ? "elf64-littlemips" : "elf32-littlemips")); |
| #endif |
| default: |
| abort (); |
| return NULL; |
| } |
| } |
| |
| /* The name of the readonly data section. */ |
| #define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \ |
| ? ".data" \ |
| : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \ |
| ? ".rdata" \ |
| : OUTPUT_FLAVOR == bfd_target_coff_flavour \ |
| ? ".rdata" \ |
| : OUTPUT_FLAVOR == bfd_target_elf_flavour \ |
| ? ".rodata" \ |
| : (abort (), "")) |
| |
| /* This is the set of options which may be modified by the .set |
| pseudo-op. We use a struct so that .set push and .set pop are more |
| reliable. */ |
| |
| struct mips_set_options |
| { |
| /* MIPS ISA (Instruction Set Architecture) level. This is set to -1 |
| if it has not been initialized. Changed by `.set mipsN', and the |
| -mipsN command line option, and the default CPU. */ |
| int isa; |
| /* Whether we are assembling for the mips16 processor. 0 if we are |
| not, 1 if we are, and -1 if the value has not been initialized. |
| Changed by `.set mips16' and `.set nomips16', and the -mips16 and |
| -nomips16 command line options, and the default CPU. */ |
| int mips16; |
| /* 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 warn_about_macros; |
| /* Non-zero if we should not move instructions. Changed by `.set |
| move', `.set volatile', `.set nomove', and `.set novolatile'. */ |
| int nomove; |
| /* Non-zero if we should not optimize branches by moving the target |
| of the branch into the delay slot. Actually, we don't perform |
| this optimization anyhow. Changed by `.set bopt' and `.set |
| nobopt'. */ |
| int nobopt; |
| /* Non-zero if we should not autoextend mips16 instructions. |
| Changed by `.set autoextend' and `.set noautoextend'. */ |
| int noautoextend; |
| }; |
| |
| /* This is the struct we use to hold the current set of options. Note |
| that we must set the isa field to ISA_UNKNOWN and the mips16 field to |
| -1 to indicate that they have not been initialized. */ |
| |
| static struct mips_set_options mips_opts = |
| { |
| ISA_UNKNOWN, -1, 0, 0, 0, 0, 0, 0 |
| }; |
| |
| /* These variables are filled in with the masks of registers used. |
| The object format code reads them and puts them in the appropriate |
| place. */ |
| unsigned long mips_gprmask; |
| unsigned long mips_cprmask[4]; |
| |
| /* MIPS ISA we are using for this output file. */ |
| static int file_mips_isa = ISA_UNKNOWN; |
| |
| /* The CPU type we are using for this output file. */ |
| static int mips_cpu = CPU_UNKNOWN; |
| |
| /* The argument of the -mabi= flag. */ |
| static char * mips_abi_string = 0; |
| |
| /* Wether we should mark the file EABI64 or EABI32. */ |
| static int mips_eabi64 = 0; |
| |
| /* If they asked for mips1 or mips2 and a cpu that is |
| mips3 or greater, then mark the object file 32BITMODE. */ |
| static int mips_32bitmode = 0; |
| |
| /* True if -mgp32 was passed. */ |
| static int mips_gp32 = 0; |
| |
| /* Some ISA's have delay slots for instructions which read or write |
| from a coprocessor (eg. mips1-mips3); some don't (eg mips4). |
| Return true if instructions marked INSN_LOAD_COPROC_DELAY, |
| INSN_COPROC_MOVE_DELAY, or INSN_WRITE_COND_CODE actually have a |
| delay slot in this ISA. The uses of this macro assume that any |
| ISA that has delay slots for one of these, has them for all. They |
| also assume that ISAs which don't have delays for these insns, don't |
| have delays for the INSN_LOAD_MEMORY_DELAY instructions either. */ |
| #define ISA_HAS_COPROC_DELAYS(ISA) ( \ |
| (ISA) == ISA_MIPS1 \ |
| || (ISA) == ISA_MIPS2 \ |
| || (ISA) == ISA_MIPS3 \ |
| ) |
| |
| /* Return true if ISA supports 64 bit gp register instructions. */ |
| #define ISA_HAS_64BIT_REGS(ISA) ( \ |
| (ISA) == ISA_MIPS3 \ |
| || (ISA) == ISA_MIPS4 \ |
| || (ISA) == ISA_MIPS5 \ |
| || (ISA) == ISA_MIPS64 \ |
| ) |
| |
| /* Whether the processor uses hardware interlocks to protect |
| reads from the HI and LO registers, and thus does not |
| require nops to be inserted. |
| |
| FIXME: GCC makes a distinction between -mcpu=FOO and -mFOO: |
| -mcpu=FOO schedules for FOO, but still produces code that meets the |
| requirements of MIPS ISA I. For example, it won't generate any |
| FOO-specific instructions, and it will still assume that any |
| scheduling hazards described in MIPS ISA I are there, even if FOO |
| has interlocks. -mFOO gives GCC permission to generate code that |
| will only run on a FOO; it will generate FOO-specific instructions, |
| and assume interlocks provided by a FOO. |
| |
| However, GAS currently doesn't make this distinction; before Jan 28 |
| 1999, GAS's -mcpu=FOO implied -mFOO, which violates GCC's |
| assumptions. The GCC driver passes these flags through to GAS, so |
| if GAS actually does anything that doesn't meet MIPS ISA I with |
| -mFOO, then GCC's -mcpu=FOO flag isn't going to work. |
| |
| And furthermore, it did not assume that -mFOO implied -mcpu=FOO, |
| which seems senseless --- why generate code which will only run on |
| a FOO, but schedule for something else? |
| |
| So now, at least, -mcpu=FOO and -mFOO are exactly equivalent. |
| |
| -- Jim Blandy <jimb@cygnus.com> */ |
| |
| #define hilo_interlocks (mips_cpu == CPU_R4010 \ |
| ) |
| |
| /* Whether the processor uses hardware interlocks to protect reads |
| from the GPRs, and thus does not require nops to be inserted. */ |
| #define gpr_interlocks \ |
| (mips_opts.isa != ISA_MIPS1 \ |
| || mips_cpu == CPU_R3900) |
| |
| /* As with other "interlocks" this is used by hardware that has FP |
| (co-processor) interlocks. */ |
| /* Itbl support may require additional care here. */ |
| #define cop_interlocks (mips_cpu == CPU_R4300 \ |
| ) |
| |
| /* Is this a mfhi or mflo instruction? */ |
| #define MF_HILO_INSN(PINFO) \ |
| ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO)) |
| |
| /* MIPS PIC level. */ |
| |
| enum mips_pic_level |
| { |
| /* Do not generate PIC code. */ |
| NO_PIC, |
| |
| /* Generate PIC code as in Irix 4. This is not implemented, and I'm |
| not sure what it is supposed to do. */ |
| IRIX4_PIC, |
| |
| /* Generate PIC code as in the SVR4 MIPS ABI. */ |
| SVR4_PIC, |
| |
| /* Generate PIC code without using a global offset table: the data |
| segment has a maximum size of 64K, all data references are off |
| the $gp register, and all text references are PC relative. This |
| is used on some embedded systems. */ |
| EMBEDDED_PIC |
| }; |
| |
| static enum mips_pic_level mips_pic; |
| |
| /* 1 if we should generate 32 bit offsets from the GP register in |
| SVR4_PIC mode. Currently has no meaning in other modes. */ |
| static int mips_big_got; |
| |
| /* 1 if trap instructions should used for overflow rather than break |
| instructions. */ |
| static int mips_trap; |
| |
| /* 1 if double width floating point constants should not be constructed |
| by a assembling two single width halves into two single width floating |
| point registers which just happen to alias the double width destination |
| register. On some architectures this aliasing can be disabled by a bit |
| in the status register, and the setting of this bit cannot be determined |
| automatically at assemble time. */ |
| static int mips_disable_float_construction; |
| |
| /* Non-zero if any .set noreorder directives were used. */ |
| |
| static int mips_any_noreorder; |
| |
| /* Non-zero if nops should be inserted when the register referenced in |
| an mfhi/mflo instruction is read in the next two instructions. */ |
| static int mips_7000_hilo_fix; |
| |
| /* The size of the small data section. */ |
| static unsigned int g_switch_value = 8; |
| /* Whether the -G option was used. */ |
| static int g_switch_seen = 0; |
| |
| #define N_RMASK 0xc4 |
| #define N_VFP 0xd4 |
| |
| /* If we can determine in advance that GP optimization won't be |
| possible, we can skip the relaxation stuff that tries to produce |
| GP-relative references. This makes delay slot optimization work |
| better. |
| |
| This function can only provide a guess, but it seems to work for |
| gcc output. It needs to guess right for gcc, otherwise gcc |
| will put what it thinks is a GP-relative instruction in a branch |
| delay slot. |
| |
| I don't know if a fix is needed for the SVR4_PIC mode. I've only |
| fixed it for the non-PIC mode. KR 95/04/07 */ |
| static int nopic_need_relax PARAMS ((symbolS *, int)); |
| |
| /* handle of the OPCODE hash table */ |
| static struct hash_control *op_hash = NULL; |
| |
| /* The opcode hash table we use for the mips16. */ |
| static struct hash_control *mips16_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. |
| */ |
| |
| static char *insn_error; |
| |
| static int auto_align = 1; |
| |
| /* When outputting SVR4 PIC code, the assembler needs to know the |
| offset in the stack frame from which to restore the $gp register. |
| This is set by the .cprestore pseudo-op, and saved in this |
| variable. */ |
| static offsetT mips_cprestore_offset = -1; |
| |
| /* This is the register which holds the stack frame, as set by the |
| .frame pseudo-op. This is needed to implement .cprestore. */ |
| static int mips_frame_reg = SP; |
| |
| /* To output NOP instructions correctly, we need to keep information |
| about the previous two instructions. */ |
| |
| /* Whether we are optimizing. The default value of 2 means to remove |
| unneeded NOPs and swap branch instructions when possible. A value |
| of 1 means to not swap branches. A value of 0 means to always |
| insert NOPs. */ |
| static int mips_optimize = 2; |
| |
| /* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is |
| equivalent to seeing no -g option at all. */ |
| static int mips_debug = 0; |
| |
| /* The previous instruction. */ |
| static struct mips_cl_insn prev_insn; |
| |
| /* The instruction before prev_insn. */ |
| static struct mips_cl_insn prev_prev_insn; |
| |
| /* If we don't want information for prev_insn or prev_prev_insn, we |
| point the insn_mo field at this dummy integer. */ |
| static const struct mips_opcode dummy_opcode = { NULL, NULL, 0, 0, 0, 0 }; |
| |
| /* Non-zero if prev_insn is valid. */ |
| static int prev_insn_valid; |
| |
| /* The frag for the previous instruction. */ |
| static struct frag *prev_insn_frag; |
| |
| /* The offset into prev_insn_frag for the previous instruction. */ |
| static long prev_insn_where; |
| |
| /* The reloc type for the previous instruction, if any. */ |
| static bfd_reloc_code_real_type prev_insn_reloc_type; |
| |
| /* The reloc for the previous instruction, if any. */ |
| static fixS *prev_insn_fixp; |
| |
| /* Non-zero if the previous instruction was in a delay slot. */ |
| static int prev_insn_is_delay_slot; |
| |
| /* Non-zero if the previous instruction was in a .set noreorder. */ |
| static int prev_insn_unreordered; |
| |
| /* Non-zero if the previous instruction uses an extend opcode (if |
| mips16). */ |
| static int prev_insn_extended; |
| |
| /* Non-zero if the previous previous instruction was in a .set |
| noreorder. */ |
| static int prev_prev_insn_unreordered; |
| |
| /* If this is set, it points to a frag holding nop instructions which |
| were inserted before the start of a noreorder section. If those |
| nops turn out to be unnecessary, the size of the frag can be |
| decreased. */ |
| static fragS *prev_nop_frag; |
| |
| /* The number of nop instructions we created in prev_nop_frag. */ |
| static int prev_nop_frag_holds; |
| |
| /* The number of nop instructions that we know we need in |
| prev_nop_frag. */ |
| static int prev_nop_frag_required; |
| |
| /* The number of instructions we've seen since prev_nop_frag. */ |
| static int prev_nop_frag_since; |
| |
| /* For ECOFF and ELF, relocations against symbols are done in two |
| parts, with a HI relocation and a LO relocation. Each relocation |
| has only 16 bits of space to store an addend. This means that in |
| order for the linker to handle carries correctly, it must be able |
| to locate both the HI and the LO relocation. This means that the |
| relocations must appear in order in the relocation table. |
| |
| In order to implement this, we keep track of each unmatched HI |
| relocation. We then sort them so that they immediately precede the |
| corresponding LO relocation. */ |
| |
| struct mips_hi_fixup |
| { |
| /* Next HI fixup. */ |
| struct mips_hi_fixup *next; |
| /* This fixup. */ |
| fixS *fixp; |
| /* The section this fixup is in. */ |
| segT seg; |
| }; |
| |
| /* The list of unmatched HI relocs. */ |
| |
| static struct mips_hi_fixup *mips_hi_fixup_list; |
| |
| /* Map normal MIPS register numbers to mips16 register numbers. */ |
| |
| #define X ILLEGAL_REG |
| static const int mips32_to_16_reg_map[] = |
| { |
| X, X, 2, 3, 4, 5, 6, 7, |
| X, X, X, X, X, X, X, X, |
| 0, 1, X, X, X, X, X, X, |
| X, X, X, X, X, X, X, X |
| }; |
| #undef X |
| |
| /* Map mips16 register numbers to normal MIPS register numbers. */ |
| |
| static const unsigned int mips16_to_32_reg_map[] = |
| { |
| 16, 17, 2, 3, 4, 5, 6, 7 |
| }; |
| |
| /* Since the MIPS does not have multiple forms of PC relative |
| instructions, we do not have to do relaxing as is done on other |
| platforms. However, we do have to handle GP relative addressing |
| correctly, which turns out to be a similar problem. |
| |
| Every macro that refers to a symbol can occur in (at least) two |
| forms, one with GP relative addressing and one without. For |
| example, loading a global variable into a register generally uses |
| a macro instruction like this: |
| lw $4,i |
| If i can be addressed off the GP register (this is true if it is in |
| the .sbss or .sdata section, or if it is known to be smaller than |
| the -G argument) this will generate the following instruction: |
| lw $4,i($gp) |
| This instruction will use a GPREL reloc. If i can not be addressed |
| off the GP register, the following instruction sequence will be used: |
| lui $at,i |
| lw $4,i($at) |
| In this case the first instruction will have a HI16 reloc, and the |
| second reloc will have a LO16 reloc. Both relocs will be against |
| the symbol i. |
| |
| The issue here is that we may not know whether i is GP addressable |
| until after we see the instruction that uses it. Therefore, we |
| want to be able to choose the final instruction sequence only at |
| the end of the assembly. This is similar to the way other |
| platforms choose the size of a PC relative instruction only at the |
| end of assembly. |
| |
| When generating position independent code we do not use GP |
| addressing in quite the same way, but the issue still arises as |
| external symbols and local symbols must be handled differently. |
| |
| We handle these issues by actually generating both possible |
| instruction sequences. The longer one is put in a frag_var with |
| type rs_machine_dependent. We encode what to do with the frag in |
| the subtype field. We encode (1) the number of existing bytes to |
| replace, (2) the number of new bytes to use, (3) the offset from |
| the start of the existing bytes to the first reloc we must generate |
| (that is, the offset is applied from the start of the existing |
| bytes after they are replaced by the new bytes, if any), (4) the |
| offset from the start of the existing bytes to the second reloc, |
| (5) whether a third reloc is needed (the third reloc is always four |
| bytes after the second reloc), and (6) whether to warn if this |
| variant is used (this is sometimes needed if .set nomacro or .set |
| noat is in effect). All these numbers are reasonably small. |
| |
| Generating two instruction sequences must be handled carefully to |
| ensure that delay slots are handled correctly. Fortunately, there |
| are a limited number of cases. When the second instruction |
| sequence is generated, append_insn is directed to maintain the |
| existing delay slot information, so it continues to apply to any |
| code after the second instruction sequence. This means that the |
| second instruction sequence must not impose any requirements not |
| required by the first instruction sequence. |
| |
| These variant frags are then handled in functions called by the |
| machine independent code. md_estimate_size_before_relax returns |
| the final size of the frag. md_convert_frag sets up the final form |
| of the frag. tc_gen_reloc adjust the first reloc and adds a second |
| one if needed. */ |
| #define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \ |
| ((relax_substateT) \ |
| (((old) << 23) \ |
| | ((new) << 16) \ |
| | (((reloc1) + 64) << 9) \ |
| | (((reloc2) + 64) << 2) \ |
| | ((reloc3) ? (1 << 1) : 0) \ |
| | ((warn) ? 1 : 0))) |
| #define RELAX_OLD(i) (((i) >> 23) & 0x7f) |
| #define RELAX_NEW(i) (((i) >> 16) & 0x7f) |
| #define RELAX_RELOC1(i) ((bfd_vma) (((i) >> 9) & 0x7f) - 64) |
| #define RELAX_RELOC2(i) ((bfd_vma) (((i) >> 2) & 0x7f) - 64) |
| #define RELAX_RELOC3(i) (((i) >> 1) & 1) |
| #define RELAX_WARN(i) ((i) & 1) |
| |
| /* For mips16 code, we use an entirely different form of relaxation. |
| mips16 supports two versions of most instructions which take |
| immediate values: a small one which takes some small value, and a |
| larger one which takes a 16 bit value. Since branches also follow |
| this pattern, relaxing these values is required. |
| |
| We can assemble both mips16 and normal MIPS code in a single |
| object. Therefore, we need to support this type of relaxation at |
| the same time that we support the relaxation described above. We |
| use the high bit of the subtype field to distinguish these cases. |
| |
| The information we store for this type of relaxation is the |
| argument code found in the opcode file for this relocation, whether |
| the user explicitly requested a small or extended form, and whether |
| the relocation is in a jump or jal delay slot. That tells us the |
| size of the value, and how it should be stored. We also store |
| whether the fragment is considered to be extended or not. We also |
| store whether this is known to be a branch to a different section, |
| whether we have tried to relax this frag yet, and whether we have |
| ever extended a PC relative fragment because of a shift count. */ |
| #define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \ |
| (0x80000000 \ |
| | ((type) & 0xff) \ |
| | ((small) ? 0x100 : 0) \ |
| | ((ext) ? 0x200 : 0) \ |
| | ((dslot) ? 0x400 : 0) \ |
| | ((jal_dslot) ? 0x800 : 0)) |
| #define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0) |
| #define RELAX_MIPS16_TYPE(i) ((i) & 0xff) |
| #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0) |
| #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0) |
| #define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0) |
| #define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0) |
| #define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0) |
| #define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000) |
| #define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000) |
| #define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0) |
| #define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000) |
| #define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000) |
| |
| /* Prototypes for static functions. */ |
| |
| #ifdef __STDC__ |
| #define internalError() \ |
| as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__) |
| #else |
| #define internalError() as_fatal (_("MIPS internal Error")); |
| #endif |
| |
| enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG }; |
| |
| static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip, |
| unsigned int reg, enum mips_regclass class)); |
| static int reg_needs_delay PARAMS ((unsigned int)); |
| static void mips16_mark_labels PARAMS ((void)); |
| static void append_insn PARAMS ((char *place, |
| struct mips_cl_insn * ip, |
| expressionS * p, |
| bfd_reloc_code_real_type r, |
| boolean)); |
| static void mips_no_prev_insn PARAMS ((int)); |
| static void mips_emit_delays PARAMS ((boolean)); |
| #ifdef USE_STDARG |
| static void macro_build PARAMS ((char *place, int *counter, expressionS * ep, |
| const char *name, const char *fmt, |
| ...)); |
| #else |
| static void macro_build (); |
| #endif |
| static void mips16_macro_build PARAMS ((char *, int *, expressionS *, |
| const char *, const char *, |
| va_list)); |
| static void macro_build_lui PARAMS ((char *place, int *counter, |
| expressionS * ep, int regnum)); |
| static void set_at PARAMS ((int *counter, int reg, int unsignedp)); |
| static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip, |
| expressionS *)); |
| static void load_register PARAMS ((int *, int, expressionS *, int)); |
| static void load_address PARAMS ((int *counter, int reg, expressionS *ep)); |
| static void macro PARAMS ((struct mips_cl_insn * ip)); |
| static void mips16_macro PARAMS ((struct mips_cl_insn * ip)); |
| #ifdef LOSING_COMPILER |
| static void macro2 PARAMS ((struct mips_cl_insn * ip)); |
| #endif |
| static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip)); |
| static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip)); |
| static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean, |
| boolean, boolean, unsigned long *, |
| boolean *, unsigned short *)); |
| static int my_getSmallExpression PARAMS ((expressionS * ep, char *str)); |
| static void my_getExpression PARAMS ((expressionS * ep, char *str)); |
| static symbolS *get_symbol PARAMS ((void)); |
| static void mips_align PARAMS ((int to, int fill, symbolS *label)); |
| static void s_align PARAMS ((int)); |
| static void s_change_sec PARAMS ((int)); |
| static void s_cons PARAMS ((int)); |
| static void s_float_cons PARAMS ((int)); |
| static void s_mips_globl PARAMS ((int)); |
| static void s_option PARAMS ((int)); |
| static void s_mipsset PARAMS ((int)); |
| static void s_abicalls PARAMS ((int)); |
| static void s_cpload PARAMS ((int)); |
| static void s_cprestore PARAMS ((int)); |
| static void s_gpword PARAMS ((int)); |
| static void s_cpadd PARAMS ((int)); |
| static void s_insn PARAMS ((int)); |
| static void md_obj_begin PARAMS ((void)); |
| static void md_obj_end PARAMS ((void)); |
| static long get_number PARAMS ((void)); |
| static void s_mips_ent PARAMS ((int)); |
| static void s_mips_end PARAMS ((int)); |
| static void s_mips_frame PARAMS ((int)); |
| static void s_mips_mask PARAMS ((int)); |
| static void s_mips_stab PARAMS ((int)); |
| static void s_mips_weakext PARAMS ((int)); |
| static void s_file PARAMS ((int)); |
| static int mips16_extended_frag PARAMS ((fragS *, asection *, long)); |
| static const char *mips_isa_to_str PARAMS ((int)); |
| static const char *mips_cpu_to_str PARAMS ((int)); |
| static int validate_mips_insn PARAMS ((const struct mips_opcode *)); |
| |
| /* Table and functions used to map between CPU/ISA names, and |
| ISA levels, and CPU numbers. */ |
| |
| struct mips_cpu_info |
| { |
| const char *name; /* CPU or ISA name. */ |
| int is_isa; /* Is this an ISA? (If 0, a CPU.) */ |
| int isa; /* ISA level. */ |
| int cpu; /* CPU number (default CPU if ISA). */ |
| }; |
| |
| static const struct mips_cpu_info *mips_cpu_info_from_name PARAMS ((const char *)); |
| static const struct mips_cpu_info *mips_cpu_info_from_isa PARAMS ((int)); |
| static const struct mips_cpu_info *mips_cpu_info_from_cpu PARAMS ((int)); |
| |
| /* Pseudo-op table. |
| |
| The following pseudo-ops from the Kane and Heinrich MIPS book |
| should be defined here, but are currently unsupported: .alias, |
| .galive, .gjaldef, .gjrlive, .livereg, .noalias. |
| |
| The following pseudo-ops from the Kane and Heinrich MIPS book are |
| specific to the type of debugging information being generated, and |
| should be defined by the object format: .aent, .begin, .bend, |
| .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp, |
| .vreg. |
| |
| The following pseudo-ops from the Kane and Heinrich MIPS book are |
| not MIPS CPU specific, but are also not specific to the object file |
| format. This file is probably the best place to define them, but |
| they are not currently supported: .asm0, .endr, .lab, .repeat, |
| .struct. */ |
| |
| static const pseudo_typeS mips_pseudo_table[] = |
| { |
| /* MIPS specific pseudo-ops. */ |
| {"option", s_option, 0}, |
| {"set", s_mipsset, 0}, |
| {"rdata", s_change_sec, 'r'}, |
| {"sdata", s_change_sec, 's'}, |
| {"livereg", s_ignore, 0}, |
| {"abicalls", s_abicalls, 0}, |
| {"cpload", s_cpload, 0}, |
| {"cprestore", s_cprestore, 0}, |
| {"gpword", s_gpword, 0}, |
| {"cpadd", s_cpadd, 0}, |
| {"insn", s_insn, 0}, |
| |
| /* Relatively generic pseudo-ops that happen to be used on MIPS |
| chips. */ |
| {"asciiz", stringer, 1}, |
| {"bss", s_change_sec, 'b'}, |
| {"err", s_err, 0}, |
| {"half", s_cons, 1}, |
| {"dword", s_cons, 3}, |
| {"weakext", s_mips_weakext, 0}, |
| |
| /* These pseudo-ops are defined in read.c, but must be overridden |
| here for one reason or another. */ |
| {"align", s_align, 0}, |
| {"byte", s_cons, 0}, |
| {"data", s_change_sec, 'd'}, |
| {"double", s_float_cons, 'd'}, |
| {"float", s_float_cons, 'f'}, |
| {"globl", s_mips_globl, 0}, |
| {"global", s_mips_globl, 0}, |
| {"hword", s_cons, 1}, |
| {"int", s_cons, 2}, |
| {"long", s_cons, 2}, |
| {"octa", s_cons, 4}, |
| {"quad", s_cons, 3}, |
| {"short", s_cons, 1}, |
| {"single", s_float_cons, 'f'}, |
| {"stabn", s_mips_stab, 'n'}, |
| {"text", s_change_sec, 't'}, |
| {"word", s_cons, 2}, |
| |
| #ifdef MIPS_STABS_ELF |
| { "extern", ecoff_directive_extern, 0}, |
| #endif |
| |
| { NULL, NULL, 0 }, |
| }; |
| |
| static const pseudo_typeS mips_nonecoff_pseudo_table[] = |
| { |
| /* These pseudo-ops should be defined by the object file format. |
| However, a.out doesn't support them, so we have versions here. */ |
| {"aent", s_mips_ent, 1}, |
| {"bgnb", s_ignore, 0}, |
| {"end", s_mips_end, 0}, |
| {"endb", s_ignore, 0}, |
| {"ent", s_mips_ent, 0}, |
| {"file", s_file, 0}, |
| {"fmask", s_mips_mask, 'F'}, |
| {"frame", s_mips_frame, 0}, |
| {"loc", s_ignore, 0}, |
| {"mask", s_mips_mask, 'R'}, |
| {"verstamp", s_ignore, 0}, |
| { NULL, NULL, 0 }, |
| }; |
| |
| extern void pop_insert PARAMS ((const pseudo_typeS *)); |
| |
| void |
| mips_pop_insert () |
| { |
| pop_insert (mips_pseudo_table); |
| if (! ECOFF_DEBUGGING) |
| pop_insert (mips_nonecoff_pseudo_table); |
| } |
| |
| /* Symbols labelling the current insn. */ |
| |
| struct insn_label_list |
| { |
| struct insn_label_list *next; |
| symbolS *label; |
| }; |
| |
| static struct insn_label_list *insn_labels; |
| static struct insn_label_list *free_insn_labels; |
| |
| static void mips_clear_insn_labels PARAMS ((void)); |
| |
| static inline void |
| mips_clear_insn_labels () |
| { |
| register struct insn_label_list **pl; |
| |
| for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next) |
| ; |
| *pl = insn_labels; |
| insn_labels = NULL; |
| } |
| |
| static char *expr_end; |
| |
| /* Expressions which appear in instructions. These are set by |
| mips_ip. */ |
| |
| static expressionS imm_expr; |
| static expressionS offset_expr; |
| |
| /* Relocs associated with imm_expr and offset_expr. */ |
| |
| static bfd_reloc_code_real_type imm_reloc; |
| static bfd_reloc_code_real_type offset_reloc; |
| |
| /* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc. */ |
| |
| static boolean imm_unmatched_hi; |
| |
| /* These are set by mips16_ip if an explicit extension is used. */ |
| |
| static boolean mips16_small, mips16_ext; |
| |
| #ifdef MIPS_STABS_ELF |
| /* The pdr segment for per procedure frame/regmask info */ |
| |
| static segT pdr_seg; |
| #endif |
| |
| static const char * |
| mips_isa_to_str (isa) |
| int isa; |
| { |
| const struct mips_cpu_info *ci; |
| static char s[20]; |
| |
| ci = mips_cpu_info_from_isa (isa); |
| if (ci != NULL) |
| return (ci->name); |
| |
| sprintf (s, "ISA#%d", isa); |
| return s; |
| } |
| |
| static const char * |
| mips_cpu_to_str (cpu) |
| int cpu; |
| { |
| const struct mips_cpu_info *ci; |
| static char s[16]; |
| |
| ci = mips_cpu_info_from_cpu (cpu); |
| if (ci != NULL) |
| return (ci->name); |
| |
| sprintf (s, "CPU#%d", cpu); |
| return s; |
| } |
| |
| /* 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 () |
| { |
| register const char *retval = NULL; |
| int i = 0; |
| const char *cpu; |
| char *a = NULL; |
| int broken = 0; |
| int mips_isa_from_cpu; |
| int target_cpu_had_mips16 = 0; |
| const struct mips_cpu_info *ci; |
| |
| /* GP relative stuff not working for PE */ |
| if (strncmp (TARGET_OS, "pe", 2) == 0 |
| && g_switch_value != 0) |
| { |
| if (g_switch_seen) |
| as_bad (_("-G not supported in this configuration.")); |
| g_switch_value = 0; |
| } |
| |
| cpu = TARGET_CPU; |
| if (strcmp (cpu + (sizeof TARGET_CPU) - 3, "el") == 0) |
| { |
| a = xmalloc (sizeof TARGET_CPU); |
| strcpy (a, TARGET_CPU); |
| a[(sizeof TARGET_CPU) - 3] = '\0'; |
| cpu = a; |
| } |
| |
| if (strncmp (cpu, "mips16", sizeof "mips16" - 1) == 0) |
| { |
| target_cpu_had_mips16 = 1; |
| cpu += sizeof "mips16" - 1; |
| } |
| |
| if (mips_opts.mips16 < 0) |
| mips_opts.mips16 = target_cpu_had_mips16; |
| |
| /* At this point, mips_cpu will either be CPU_UNKNOWN if no CPU was |
| specified on the command line, or some other value if one was. |
| Similarly, mips_opts.isa will be ISA_UNKNOWN if not specified on |
| the command line, or will be set otherwise if one was. */ |
| if (mips_cpu != CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN) |
| { |
| /* We have it all. There's nothing to do. */ |
| } |
| else if (mips_cpu != CPU_UNKNOWN && mips_opts.isa == ISA_UNKNOWN) |
| { |
| /* We have CPU, we need ISA. */ |
| ci = mips_cpu_info_from_cpu (mips_cpu); |
| assert (ci != NULL); |
| mips_opts.isa = ci->isa; |
| } |
| else if (mips_cpu == CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN) |
| { |
| /* We have ISA, we need default CPU. */ |
| ci = mips_cpu_info_from_isa (mips_opts.isa); |
| assert (ci != NULL); |
| mips_cpu = ci->cpu; |
| } |
| else |
| { |
| /* We need to set both ISA and CPU from target cpu. */ |
| ci = mips_cpu_info_from_name (cpu); |
| if (ci == NULL) |
| ci = mips_cpu_info_from_cpu (CPU_R3000); |
| assert (ci != NULL); |
| mips_opts.isa = ci->isa; |
| mips_cpu = ci->cpu; |
| } |
| |
| ci = mips_cpu_info_from_cpu (mips_cpu); |
| assert (ci != NULL); |
| mips_isa_from_cpu = ci->isa; |
| |
| /* End of TARGET_CPU processing, get rid of malloced memory |
| if necessary. */ |
| cpu = NULL; |
| if (a != NULL) |
| { |
| free (a); |
| a = NULL; |
| } |
| |
| if (mips_opts.isa == ISA_MIPS1 && mips_trap) |
| as_bad (_("trap exception not supported at ISA 1")); |
| |
| /* Set the EABI kind based on the ISA before the user gets |
| to change the ISA with directives. This isn't really |
| the best, but then neither is basing the abi on the isa. */ |
| if (ISA_HAS_64BIT_REGS (mips_opts.isa) |
| && mips_abi_string |
| && 0 == strcmp (mips_abi_string, "eabi")) |
| mips_eabi64 = 1; |
| |
| /* If they asked for mips1 or mips2 and a cpu that is |
| mips3 or greater, then mark the object file 32BITMODE. */ |
| if (mips_isa_from_cpu != ISA_UNKNOWN |
| && ! ISA_HAS_64BIT_REGS (mips_opts.isa) |
| && ISA_HAS_64BIT_REGS (mips_isa_from_cpu)) |
| mips_32bitmode = 1; |
| |
| if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_cpu)) |
| as_warn (_("Could not set architecture and machine")); |
| |
| file_mips_isa = mips_opts.isa; |
| |
| op_hash = hash_new (); |
| |
| for (i = 0; i < NUMOPCODES;) |
| { |
| const char *name = mips_opcodes[i].name; |
| |
| retval = hash_insert (op_hash, name, (PTR) &mips_opcodes[i]); |
| if (retval != NULL) |
| { |
| fprintf (stderr, _("internal error: can't hash `%s': %s\n"), |
| mips_opcodes[i].name, retval); |
| /* Probably a memory allocation problem? Give up now. */ |
| as_fatal (_("Broken assembler. No assembly attempted.")); |
| } |
| do |
| { |
| if (mips_opcodes[i].pinfo != INSN_MACRO) |
| { |
| if (!validate_mips_insn (&mips_opcodes[i])) |
| broken = 1; |
| } |
| ++i; |
| } |
| while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name)); |
| } |
| |
| mips16_op_hash = hash_new (); |
| |
| i = 0; |
| while (i < bfd_mips16_num_opcodes) |
| { |
| const char *name = mips16_opcodes[i].name; |
| |
| retval = hash_insert (mips16_op_hash, name, (PTR) &mips16_opcodes[i]); |
| if (retval != NULL) |
| as_fatal (_("internal: can't hash `%s': %s"), |
| mips16_opcodes[i].name, retval); |
| do |
| { |
| if (mips16_opcodes[i].pinfo != INSN_MACRO |
| && ((mips16_opcodes[i].match & mips16_opcodes[i].mask) |
| != mips16_opcodes[i].match)) |
| { |
| fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"), |
| mips16_opcodes[i].name, mips16_opcodes[i].args); |
| broken = 1; |
| } |
| ++i; |
| } |
| while (i < bfd_mips16_num_opcodes |
| && strcmp (mips16_opcodes[i].name, name) == 0); |
| } |
| |
| 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; i < 32; i++) |
| { |
| char buf[5]; |
| |
| sprintf (buf, "$%d", i); |
| symbol_table_insert (symbol_new (buf, reg_section, i, |
| &zero_address_frag)); |
| } |
| symbol_table_insert (symbol_new ("$fp", reg_section, FP, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$sp", reg_section, SP, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$gp", reg_section, GP, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$at", reg_section, AT, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$kt0", reg_section, KT0, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$kt1", reg_section, KT1, |
| &zero_address_frag)); |
| symbol_table_insert (symbol_new ("$pc", reg_section, -1, |
| &zero_address_frag)); |
| |
| mips_no_prev_insn (false); |
| |
| mips_gprmask = 0; |
| mips_cprmask[0] = 0; |
| mips_cprmask[1] = 0; |
| mips_cprmask[2] = 0; |
| mips_cprmask[3] = 0; |
| |
| /* set the default alignment for the text section (2**2) */ |
| record_alignment (text_section, 2); |
| |
| if (USE_GLOBAL_POINTER_OPT) |
| bfd_set_gp_size (stdoutput, g_switch_value); |
| |
| if (OUTPUT_FLAVOR == bfd_target_elf_flavour) |
| { |
| /* On a native system, sections must be aligned to 16 byte |
| boundaries. When configured for an embedded ELF target, we |
| don't bother. */ |
| if (strcmp (TARGET_OS, "elf") != 0) |
| { |
| (void) bfd_set_section_alignment (stdoutput, text_section, 4); |
| (void) bfd_set_section_alignment (stdoutput, data_section, 4); |
| (void) bfd_set_section_alignment (stdoutput, bss_section, 4); |
| } |
| |
| /* Create a .reginfo section for register masks and a .mdebug |
| section for debugging information. */ |
| { |
| segT seg; |
| subsegT subseg; |
| flagword flags; |
| segT sec; |
| |
| seg = now_seg; |
| subseg = now_subseg; |
| |
| /* The ABI says this section should be loaded so that the |
| running program can access it. However, we don't load it |
| if we are configured for an embedded target */ |
| flags = SEC_READONLY | SEC_DATA; |
| if (strcmp (TARGET_OS, "elf") != 0) |
| flags |= SEC_ALLOC | SEC_LOAD; |
| |
| if (! mips_64) |
| { |
| sec = subseg_new (".reginfo", (subsegT) 0); |
| |
| (void) bfd_set_section_flags (stdoutput, sec, flags); |
| (void) bfd_set_section_alignment (stdoutput, sec, 2); |
| |
| #ifdef OBJ_ELF |
| mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo)); |
| #endif |
| } |
| else |
| { |
| /* The 64-bit ABI uses a .MIPS.options section rather than |
| .reginfo section. */ |
| sec = subseg_new (".MIPS.options", (subsegT) 0); |
| (void) bfd_set_section_flags (stdoutput, sec, flags); |
| (void) bfd_set_section_alignment (stdoutput, sec, 3); |
| |
| #ifdef OBJ_ELF |
| /* Set up the option header. */ |
| { |
| Elf_Internal_Options opthdr; |
| char *f; |
| |
| opthdr.kind = ODK_REGINFO; |
| opthdr.size = (sizeof (Elf_External_Options) |
| + sizeof (Elf64_External_RegInfo)); |
| opthdr.section = 0; |
| opthdr.info = 0; |
| f = frag_more (sizeof (Elf_External_Options)); |
| bfd_mips_elf_swap_options_out (stdoutput, &opthdr, |
| (Elf_External_Options *) f); |
| |
| mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo)); |
| } |
| #endif |
| } |
| |
| if (ECOFF_DEBUGGING) |
| { |
| sec = subseg_new (".mdebug", (subsegT) 0); |
| (void) bfd_set_section_flags (stdoutput, sec, |
| SEC_HAS_CONTENTS | SEC_READONLY); |
| (void) bfd_set_section_alignment (stdoutput, sec, 2); |
| } |
| |
| #ifdef MIPS_STABS_ELF |
| pdr_seg = subseg_new (".pdr", (subsegT) 0); |
| (void) bfd_set_section_flags (stdoutput, pdr_seg, |
| SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); |
| (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2); |
| #endif |
| |
| subseg_set (seg, subseg); |
| } |
| } |
| |
| if (! ECOFF_DEBUGGING) |
| md_obj_begin (); |
| } |
| |
| void |
| md_mips_end () |
| { |
| if (! ECOFF_DEBUGGING) |
| md_obj_end (); |
| } |
| |
| void |
| md_assemble (str) |
| char *str; |
| { |
| struct mips_cl_insn insn; |
| |
| imm_expr.X_op = O_absent; |
| imm_reloc = BFD_RELOC_UNUSED; |
| imm_unmatched_hi = false; |
| offset_expr.X_op = O_absent; |
| offset_reloc = BFD_RELOC_UNUSED; |
| |
| if (mips_opts.mips16) |
| mips16_ip (str, &insn); |
| else |
| { |
| mips_ip (str, &insn); |
| DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"), |
| str, insn.insn_opcode)); |
| } |
| |
| if (insn_error) |
| { |
| as_bad ("%s `%s'", insn_error, str); |
| return; |
| } |
| |
| if (insn.insn_mo->pinfo == INSN_MACRO) |
| { |
| if (mips_opts.mips16) |
| mips16_macro (&insn); |
| else |
| macro (&insn); |
| } |
| else |
| { |
| if (imm_expr.X_op != O_absent) |
| append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc, |
| imm_unmatched_hi); |
| else if (offset_expr.X_op != O_absent) |
| append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false); |
| else |
| append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false); |
| } |
| } |
| |
| /* See whether instruction IP reads register REG. CLASS is the type |
| of register. */ |
| |
| static int |
| insn_uses_reg (ip, reg, class) |
| struct mips_cl_insn *ip; |
| unsigned int reg; |
| enum mips_regclass class; |
| { |
| if (class == MIPS16_REG) |
| { |
| assert (mips_opts.mips16); |
| reg = mips16_to_32_reg_map[reg]; |
| class = MIPS_GR_REG; |
| } |
| |
| /* Don't report on general register 0, since it never changes. */ |
| if (class == MIPS_GR_REG && reg == 0) |
| return 0; |
| |
| if (class == MIPS_FP_REG) |
| { |
| assert (! mips_opts.mips16); |
| /* If we are called with either $f0 or $f1, we must check $f0. |
| This is not optimal, because it will introduce an unnecessary |
| NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would |
| need to distinguish reading both $f0 and $f1 or just one of |
| them. Note that we don't have to check the other way, |
| because there is no instruction that sets both $f0 and $f1 |
| and requires a delay. */ |
| if ((ip->insn_mo->pinfo & INSN_READ_FPR_S) |
| && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1) |
| == (reg &~ (unsigned) 1))) |
| return 1; |
| if ((ip->insn_mo->pinfo & INSN_READ_FPR_T) |
| && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1) |
| == (reg &~ (unsigned) 1))) |
| return 1; |
| } |
| else if (! mips_opts.mips16) |
| { |
| if ((ip->insn_mo->pinfo & INSN_READ_GPR_S) |
| && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg) |
| return 1; |
| if ((ip->insn_mo->pinfo & INSN_READ_GPR_T) |
| && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg) |
| return 1; |
| } |
| else |
| { |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X) |
| && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX) |
| & MIPS16OP_MASK_RX)] |
| == reg)) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y) |
| && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RY) |
| & MIPS16OP_MASK_RY)] |
| == reg)) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z) |
| && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z) |
| & MIPS16OP_MASK_MOVE32Z)] |
| == reg)) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA) |
| return 1; |
| if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X) |
| && ((ip->insn_opcode >> MIPS16OP_SH_REGR32) |
| & MIPS16OP_MASK_REGR32) == reg) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* This function returns true if modifying a register requires a |
| delay. */ |
| |
| static int |
| reg_needs_delay (reg) |
| unsigned int reg; |
| { |
| unsigned long prev_pinfo; |
| |
| prev_pinfo = prev_insn.insn_mo->pinfo; |
| if (! mips_opts.noreorder |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && ((prev_pinfo & INSN_LOAD_COPROC_DELAY) |
| || (! gpr_interlocks |
| && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) |
| { |
| /* A load from a coprocessor or from memory. All load |
| delays delay the use of general register rt for one |
| instruction on the r3000. The r6000 and r4000 use |
| interlocks. */ |
| /* Itbl support may require additional care here. */ |
| know (prev_pinfo & INSN_WRITE_GPR_T); |
| if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Mark instruction labels in mips16 mode. This permits the linker to |
| handle them specially, such as generating jalx instructions when |
| needed. We also make them odd for the duration of the assembly, in |
| order to generate the right sort of code. We will make them even |
| in the adjust_symtab routine, while leaving them marked. This is |
| convenient for the debugger and the disassembler. The linker knows |
| to make them odd again. */ |
| |
| static void |
| mips16_mark_labels () |
| { |
| if (mips_opts.mips16) |
| { |
| struct insn_label_list *l; |
| valueT val; |
| |
| for (l = insn_labels; l != NULL; l = l->next) |
| { |
| #ifdef OBJ_ELF |
| if (OUTPUT_FLAVOR == bfd_target_elf_flavour) |
| S_SET_OTHER (l->label, STO_MIPS16); |
| #endif |
| val = S_GET_VALUE (l->label); |
| if ((val & 1) == 0) |
| S_SET_VALUE (l->label, val + 1); |
| } |
| } |
| } |
| |
| /* Output an instruction. PLACE is where to put the instruction; if |
| it is NULL, this uses frag_more to get room. IP is the instruction |
| information. ADDRESS_EXPR is an operand of the instruction to be |
| used with RELOC_TYPE. */ |
| |
| static void |
| append_insn (place, ip, address_expr, reloc_type, unmatched_hi) |
| char *place; |
| struct mips_cl_insn *ip; |
| expressionS *address_expr; |
| bfd_reloc_code_real_type reloc_type; |
| boolean unmatched_hi; |
| { |
| register unsigned long prev_pinfo, pinfo; |
| char *f; |
| fixS *fixp; |
| int nops = 0; |
| |
| /* Mark instruction labels in mips16 mode. */ |
| if (mips_opts.mips16) |
| mips16_mark_labels (); |
| |
| prev_pinfo = prev_insn.insn_mo->pinfo; |
| pinfo = ip->insn_mo->pinfo; |
| |
| if (place == NULL && (! mips_opts.noreorder || prev_nop_frag != NULL)) |
| { |
| int prev_prev_nop; |
| |
| /* If the previous insn required any delay slots, see if we need |
| to insert a NOP or two. There are eight kinds of possible |
| hazards, of which an instruction can have at most one type. |
| (1) a load from memory delay |
| (2) a load from a coprocessor delay |
| (3) an unconditional branch delay |
| (4) a conditional branch delay |
| (5) a move to coprocessor register delay |
| (6) a load coprocessor register from memory delay |
| (7) a coprocessor condition code delay |
| (8) a HI/LO special register delay |
| |
| There are a lot of optimizations we could do that we don't. |
| In particular, we do not, in general, reorder instructions. |
| If you use gcc with optimization, it will reorder |
| instructions and generally do much more optimization then we |
| do here; repeating all that work in the assembler would only |
| benefit hand written assembly code, and does not seem worth |
| it. */ |
| |
| /* This is how a NOP is emitted. */ |
| #define emit_nop() \ |
| (mips_opts.mips16 \ |
| ? md_number_to_chars (frag_more (2), 0x6500, 2) \ |
| : md_number_to_chars (frag_more (4), 0, 4)) |
| |
| /* The previous insn might require a delay slot, depending upon |
| the contents of the current insn. */ |
| if (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (((prev_pinfo & INSN_LOAD_COPROC_DELAY) |
| && ! cop_interlocks) |
| || (! gpr_interlocks |
| && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) |
| { |
| /* A load from a coprocessor or from memory. All load |
| delays delay the use of general register rt for one |
| instruction on the r3000. The r6000 and r4000 use |
| interlocks. */ |
| /* Itbl support may require additional care here. */ |
| know (prev_pinfo & INSN_WRITE_GPR_T); |
| if (mips_optimize == 0 |
| || insn_uses_reg (ip, |
| ((prev_insn.insn_opcode >> OP_SH_RT) |
| & OP_MASK_RT), |
| MIPS_GR_REG)) |
| ++nops; |
| } |
| else if (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (((prev_pinfo & INSN_COPROC_MOVE_DELAY) |
| && ! cop_interlocks) |
| || (mips_opts.isa == ISA_MIPS1 |
| && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)))) |
| { |
| /* A generic coprocessor delay. The previous instruction |
| modified a coprocessor general or control register. If |
| it modified a control register, we need to avoid any |
| coprocessor instruction (this is probably not always |
| required, but it sometimes is). If it modified a general |
| register, we avoid using that register. |
| |
| On the r6000 and r4000 loading a coprocessor register |
| from memory is interlocked, and does not require a delay. |
| |
| This case is not handled very well. There is no special |
| knowledge of CP0 handling, and the coprocessors other |
| than the floating point unit are not distinguished at |
| all. */ |
| /* Itbl support may require additional care here. FIXME! |
| Need to modify this to include knowledge about |
| user specified delays! */ |
| if (prev_pinfo & INSN_WRITE_FPR_T) |
| { |
| if (mips_optimize == 0 |
| || insn_uses_reg (ip, |
| ((prev_insn.insn_opcode >> OP_SH_FT) |
| & OP_MASK_FT), |
| MIPS_FP_REG)) |
| ++nops; |
| } |
| else if (prev_pinfo & INSN_WRITE_FPR_S) |
| { |
| if (mips_optimize == 0 |
| || insn_uses_reg (ip, |
| ((prev_insn.insn_opcode >> OP_SH_FS) |
| & OP_MASK_FS), |
| MIPS_FP_REG)) |
| ++nops; |
| } |
| else |
| { |
| /* We don't know exactly what the previous instruction |
| does. If the current instruction uses a coprocessor |
| register, we must insert a NOP. If previous |
| instruction may set the condition codes, and the |
| current instruction uses them, we must insert two |
| NOPS. */ |
| /* Itbl support may require additional care here. */ |
| if (mips_optimize == 0 |
| || ((prev_pinfo & INSN_WRITE_COND_CODE) |
| && (pinfo & INSN_READ_COND_CODE))) |
| nops += 2; |
| else if (pinfo & INSN_COP) |
| ++nops; |
| } |
| } |
| else if (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (prev_pinfo & INSN_WRITE_COND_CODE) |
| && ! cop_interlocks) |
| { |
| /* The previous instruction sets the coprocessor condition |
| codes, but does not require a general coprocessor delay |
| (this means it is a floating point comparison |
| instruction). If this instruction uses the condition |
| codes, we need to insert a single NOP. */ |
| /* Itbl support may require additional care here. */ |
| if (mips_optimize == 0 |
| || (pinfo & INSN_READ_COND_CODE)) |
| ++nops; |
| } |
| |
| /* If we're fixing up mfhi/mflo for the r7000 and the |
| previous insn was an mfhi/mflo and the current insn |
| reads the register that the mfhi/mflo wrote to, then |
| insert two nops. */ |
| |
| else if (mips_7000_hilo_fix |
| && MF_HILO_INSN (prev_pinfo) |
| && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD) |
| & OP_MASK_RD), |
| MIPS_GR_REG)) |
| { |
| nops += 2; |
| } |
| |
| /* If we're fixing up mfhi/mflo for the r7000 and the |
| 2nd previous insn was an mfhi/mflo and the current insn |
| reads the register that the mfhi/mflo wrote to, then |
| insert one nop. */ |
| |
| else if (mips_7000_hilo_fix |
| && MF_HILO_INSN (prev_prev_insn.insn_opcode) |
| && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD) |
| & OP_MASK_RD), |
| MIPS_GR_REG)) |
| |
| { |
| nops += 1; |
| } |
| |
| else if (prev_pinfo & INSN_READ_LO) |
| { |
| /* The previous instruction reads the LO register; if the |
| current instruction writes to the LO register, we must |
| insert two NOPS. Some newer processors have interlocks. |
| Also the tx39's multiply instructions can be exectuted |
| immediatly after a read from HI/LO (without the delay), |
| though the tx39's divide insns still do require the |
| delay. */ |
| if (! (hilo_interlocks |
| || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT))) |
| && (mips_optimize == 0 |
| || (pinfo & INSN_WRITE_LO))) |
| nops += 2; |
| /* Most mips16 branch insns don't have a delay slot. |
| If a read from LO is immediately followed by a branch |
| to a write to LO we have a read followed by a write |
| less than 2 insns away. We assume the target of |
| a branch might be a write to LO, and insert a nop |
| between a read and an immediately following branch. */ |
| else if (mips_opts.mips16 |
| && (mips_optimize == 0 |
| || (pinfo & MIPS16_INSN_BRANCH))) |
| nops += 1; |
| } |
| else if (prev_insn.insn_mo->pinfo & INSN_READ_HI) |
| { |
| /* The previous instruction reads the HI register; if the |
| current instruction writes to the HI register, we must |
| insert a NOP. Some newer processors have interlocks. |
| Also the note tx39's multiply above. */ |
| if (! (hilo_interlocks |
| || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT))) |
| && (mips_optimize == 0 |
| || (pinfo & INSN_WRITE_HI))) |
| nops += 2; |
| /* Most mips16 branch insns don't have a delay slot. |
| If a read from HI is immediately followed by a branch |
| to a write to HI we have a read followed by a write |
| less than 2 insns away. We assume the target of |
| a branch might be a write to HI, and insert a nop |
| between a read and an immediately following branch. */ |
| else if (mips_opts.mips16 |
| && (mips_optimize == 0 |
| || (pinfo & MIPS16_INSN_BRANCH))) |
| nops += 1; |
| } |
| |
| /* If the previous instruction was in a noreorder section, then |
| we don't want to insert the nop after all. */ |
| /* Itbl support may require additional care here. */ |
| if (prev_insn_unreordered) |
| nops = 0; |
| |
| /* There are two cases which require two intervening |
| instructions: 1) setting the condition codes using a move to |
| coprocessor instruction which requires a general coprocessor |
| delay and then reading the condition codes 2) reading the HI |
| or LO register and then writing to it (except on processors |
| which have interlocks). If we are not already emitting a NOP |
| instruction, we must check for these cases compared to the |
| instruction previous to the previous instruction. */ |
| if ((! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY) |
| && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE) |
| && (pinfo & INSN_READ_COND_CODE) |
| && ! cop_interlocks) |
| || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO) |
| && (pinfo & INSN_WRITE_LO) |
| && ! (hilo_interlocks |
| || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT)))) |
| || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) |
| && (pinfo & INSN_WRITE_HI) |
| && ! (hilo_interlocks |
| || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT))))) |
| prev_prev_nop = 1; |
| else |
| prev_prev_nop = 0; |
| |
| if (prev_prev_insn_unreordered) |
| prev_prev_nop = 0; |
| |
| if (prev_prev_nop && nops == 0) |
| ++nops; |
| |
| /* If we are being given a nop instruction, don't bother with |
| one of the nops we would otherwise output. This will only |
| happen when a nop instruction is used with mips_optimize set |
| to 0. */ |
| if (nops > 0 |
| && ! mips_opts.noreorder |
| && ip->insn_opcode == (unsigned) (mips_opts.mips16 ? 0x6500 : 0)) |
| --nops; |
| |
| /* Now emit the right number of NOP instructions. */ |
| if (nops > 0 && ! mips_opts.noreorder) |
| { |
| fragS *old_frag; |
| unsigned long old_frag_offset; |
| int i; |
| struct insn_label_list *l; |
| |
| old_frag = frag_now; |
| old_frag_offset = frag_now_fix (); |
| |
| for (i = 0; i < nops; i++) |
| emit_nop (); |
| |
| if (listing) |
| { |
| listing_prev_line (); |
| /* We may be at the start of a variant frag. In case we |
| are, make sure there is enough space for the frag |
| after the frags created by listing_prev_line. The |
| argument to frag_grow here must be at least as large |
| as the argument to all other calls to frag_grow in |
| this file. We don't have to worry about being in the |
| middle of a variant frag, because the variants insert |
| all needed nop instructions themselves. */ |
| frag_grow (40); |
| } |
| |
| for (l = insn_labels; l != NULL; l = l->next) |
| { |
| valueT val; |
| |
| assert (S_GET_SEGMENT (l->label) == now_seg); |
| symbol_set_frag (l->label, frag_now); |
| val = (valueT) frag_now_fix (); |
| /* mips16 text labels are stored as odd. */ |
| if (mips_opts.mips16) |
| val += 1; |
| S_SET_VALUE (l->label, val); |
| } |
| |
| #ifndef NO_ECOFF_DEBUGGING |
| if (ECOFF_DEBUGGING) |
| ecoff_fix_loc (old_frag, old_frag_offset); |
| #endif |
| } |
| else if (prev_nop_frag != NULL) |
| { |
| /* We have a frag holding nops we may be able to remove. If |
| we don't need any nops, we can decrease the size of |
| prev_nop_frag by the size of one instruction. If we do |
| need some nops, we count them in prev_nops_required. */ |
| if (prev_nop_frag_since == 0) |
| { |
| if (nops == 0) |
| { |
| prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4; |
| --prev_nop_frag_holds; |
| } |
| else |
| prev_nop_frag_required += nops; |
| } |
| else |
| { |
| if (prev_prev_nop == 0) |
| { |
| prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4; |
| --prev_nop_frag_holds; |
| } |
| else |
| ++prev_nop_frag_required; |
| } |
| |
| if (prev_nop_frag_holds <= prev_nop_frag_required) |
| prev_nop_frag = NULL; |
| |
| ++prev_nop_frag_since; |
| |
| /* Sanity check: by the time we reach the second instruction |
| after prev_nop_frag, we should have used up all the nops |
| one way or another. */ |
| assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL); |
| } |
| } |
| |
| if (reloc_type > BFD_RELOC_UNUSED) |
| { |
| /* We need to set up a variant frag. */ |
| assert (mips_opts.mips16 && address_expr != NULL); |
| f = frag_var (rs_machine_dependent, 4, 0, |
| RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED, |
| mips16_small, mips16_ext, |
| (prev_pinfo |
| & INSN_UNCOND_BRANCH_DELAY), |
| (prev_insn_reloc_type |
| == BFD_RELOC_MIPS16_JMP)), |
| make_expr_symbol (address_expr), (offsetT) 0, |
| (char *) NULL); |
| } |
| else if (place != NULL) |
| f = place; |
| else if (mips_opts.mips16 |
| && ! ip->use_extend |
| && reloc_type != BFD_RELOC_MIPS16_JMP) |
| { |
| /* Make sure there is enough room to swap this instruction with |
| a following jump instruction. */ |
| frag_grow (6); |
| f = frag_more (2); |
| } |
| else |
| { |
| if (mips_opts.mips16 |
| && mips_opts.noreorder |
| && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) |
| as_warn (_("extended instruction in delay slot")); |
| |
| f = frag_more (4); |
| } |
| |
| fixp = NULL; |
| if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED) |
| { |
| if (address_expr->X_op == O_constant) |
| { |
| switch (reloc_type) |
| { |
| case BFD_RELOC_32: |
| ip->insn_opcode |= address_expr->X_add_number; |
| break; |
| |
| case BFD_RELOC_LO16: |
| ip->insn_opcode |= address_expr->X_add_number & 0xffff; |
| break; |
| |
| case BFD_RELOC_MIPS_JMP: |
| if ((address_expr->X_add_number & 3) != 0) |
| as_bad (_("jump to misaligned address (0x%lx)"), |
| (unsigned long) address_expr->X_add_number); |
| ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff; |
| break; |
| |
| case BFD_RELOC_MIPS16_JMP: |
| if ((address_expr->X_add_number & 3) != 0) |
| as_bad (_("jump to misaligned address (0x%lx)"), |
| (unsigned long) address_expr->X_add_number); |
| ip->insn_opcode |= |
| (((address_expr->X_add_number & 0x7c0000) << 3) |
| | ((address_expr->X_add_number & 0xf800000) >> 7) |
| | ((address_expr->X_add_number & 0x3fffc) >> 2)); |
| break; |
| |
| case BFD_RELOC_16_PCREL_S2: |
| goto need_reloc; |
| |
| default: |
| internalError (); |
| } |
| } |
| else |
| { |
| need_reloc: |
| /* Don't generate a reloc if we are writing into a variant |
| frag. */ |
| if (place == NULL) |
| { |
| fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4, |
| address_expr, |
| reloc_type == BFD_RELOC_16_PCREL_S2, |
| reloc_type); |
| if (unmatched_hi) |
| { |
| struct mips_hi_fixup *hi_fixup; |
| |
| assert (reloc_type == BFD_RELOC_HI16_S); |
| hi_fixup = ((struct mips_hi_fixup *) |
| xmalloc (sizeof (struct mips_hi_fixup))); |
| hi_fixup->fixp = fixp; |
| hi_fixup->seg = now_seg; |
| hi_fixup->next = mips_hi_fixup_list; |
| mips_hi_fixup_list = hi_fixup; |
| } |
| } |
| } |
| } |
| |
| if (! mips_opts.mips16) |
| md_number_to_chars (f, ip->insn_opcode, 4); |
| else if (reloc_type == BFD_RELOC_MIPS16_JMP) |
| { |
| md_number_to_chars (f, ip->insn_opcode >> 16, 2); |
| md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2); |
| } |
| else |
| { |
| if (ip->use_extend) |
| { |
| md_number_to_chars (f, 0xf000 | ip->extend, 2); |
| f += 2; |
| } |
| md_number_to_chars (f, ip->insn_opcode, 2); |
| } |
| |
| /* Update the register mask information. */ |
| if (! mips_opts.mips16) |
| { |
| if (pinfo & INSN_WRITE_GPR_D) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD); |
| if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT); |
| if (pinfo & INSN_READ_GPR_S) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS); |
| if (pinfo & INSN_WRITE_GPR_31) |
| mips_gprmask |= 1 << 31; |
| if (pinfo & INSN_WRITE_FPR_D) |
| mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD); |
| if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0) |
| mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS); |
| if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) |
| mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT); |
| if ((pinfo & INSN_READ_FPR_R) != 0) |
| mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR); |
| if (pinfo & INSN_COP) |
| { |
| /* We don't keep enough information to sort these cases out. |
| The itbl support does keep this information however, although |
| we currently don't support itbl fprmats as part of the cop |
| instruction. May want to add this support in the future. */ |
| } |
| /* Never set the bit for $0, which is always zero. */ |
| mips_gprmask &= ~1 << 0; |
| } |
| else |
| { |
| if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X)) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX) |
| & MIPS16OP_MASK_RX); |
| if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y)) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY) |
| & MIPS16OP_MASK_RY); |
| if (pinfo & MIPS16_INSN_WRITE_Z) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ) |
| & MIPS16OP_MASK_RZ); |
| if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T)) |
| mips_gprmask |= 1 << TREG; |
| if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP)) |
| mips_gprmask |= 1 << SP; |
| if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31)) |
| mips_gprmask |= 1 << RA; |
| if (pinfo & MIPS16_INSN_WRITE_GPR_Y) |
| mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode); |
| if (pinfo & MIPS16_INSN_READ_Z) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z) |
| & MIPS16OP_MASK_MOVE32Z); |
| if (pinfo & MIPS16_INSN_READ_GPR_X) |
| mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32) |
| & MIPS16OP_MASK_REGR32); |
| } |
| |
| if (place == NULL && ! mips_opts.noreorder) |
| { |
| /* Filling the branch delay slot is more complex. We try to |
| switch the branch with the previous instruction, which we can |
| do if the previous instruction does not set up a condition |
| that the branch tests and if the branch is not itself the |
| target of any branch. */ |
| if ((pinfo & INSN_UNCOND_BRANCH_DELAY) |
| || (pinfo & INSN_COND_BRANCH_DELAY)) |
| { |
| if (mips_optimize < 2 |
| /* If we have seen .set volatile or .set nomove, don't |
| optimize. */ |
| || mips_opts.nomove != 0 |
| /* If we had to emit any NOP instructions, then we |
| already know we can not swap. */ |
| || nops != 0 |
| /* If we don't even know the previous insn, we can not |
| swap. */ |
| || ! prev_insn_valid |
| /* If the previous insn is already in a branch delay |
| slot, then we can not swap. */ |
| || prev_insn_is_delay_slot |
| /* If the previous previous insn was in a .set |
| noreorder, we can't swap. Actually, the MIPS |
| assembler will swap in this situation. However, gcc |
| configured -with-gnu-as will generate code like |
| .set noreorder |
| lw $4,XXX |
| .set reorder |
| INSN |
| bne $4,$0,foo |
| in which we can not swap the bne and INSN. If gcc is |
| not configured -with-gnu-as, it does not output the |
| .set pseudo-ops. We don't have to check |
| prev_insn_unreordered, because prev_insn_valid will |
| be 0 in that case. We don't want to use |
| prev_prev_insn_valid, because we do want to be able |
| to swap at the start of a function. */ |
| || prev_prev_insn_unreordered |
| /* If the branch is itself the target of a branch, we |
| can not swap. We cheat on this; all we check for is |
| whether there is a label on this instruction. If |
| there are any branches to anything other than a |
| label, users must use .set noreorder. */ |
| || insn_labels != NULL |
| /* If the previous instruction is in a variant frag, we |
| can not do the swap. This does not apply to the |
| mips16, which uses variant frags for different |
| purposes. */ |
| || (! mips_opts.mips16 |
| && prev_insn_frag->fr_type == rs_machine_dependent) |
| /* If the branch reads the condition codes, we don't |
| even try to swap, because in the sequence |
| ctc1 $X,$31 |
| INSN |
| INSN |
| bc1t LABEL |
| we can not swap, and I don't feel like handling that |
| case. */ |
| || (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (pinfo & INSN_READ_COND_CODE)) |
| /* We can not swap with an instruction that requires a |
| delay slot, becase the target of the branch might |
| interfere with that instruction. */ |
| || (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (prev_pinfo |
| /* Itbl support may require additional care here. */ |
| & (INSN_LOAD_COPROC_DELAY |
| | INSN_COPROC_MOVE_DELAY |
| | INSN_WRITE_COND_CODE))) |
| || (! (hilo_interlocks |
| || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT))) |
| && (prev_pinfo |
| & (INSN_READ_LO |
| | INSN_READ_HI))) |
| || (! mips_opts.mips16 |
| && ! gpr_interlocks |
| && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)) |
| || (! mips_opts.mips16 |
| && mips_opts.isa == ISA_MIPS1 |
| /* Itbl support may require additional care here. */ |
| && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)) |
| /* We can not swap with a branch instruction. */ |
| || (prev_pinfo |
| & (INSN_UNCOND_BRANCH_DELAY |
| | INSN_COND_BRANCH_DELAY |
| | INSN_COND_BRANCH_LIKELY)) |
| /* We do not swap with a trap instruction, since it |
| complicates trap handlers to have the trap |
| instruction be in a delay slot. */ |
| || (prev_pinfo & INSN_TRAP) |
| /* If the branch reads a register that the previous |
| instruction sets, we can not swap. */ |
| || (! mips_opts.mips16 |
| && (prev_pinfo & INSN_WRITE_GPR_T) |
| && insn_uses_reg (ip, |
| ((prev_insn.insn_opcode >> OP_SH_RT) |
| & OP_MASK_RT), |
| MIPS_GR_REG)) |
| || (! mips_opts.mips16 |
| && (prev_pinfo & INSN_WRITE_GPR_D) |
| && insn_uses_reg (ip, |
| ((prev_insn.insn_opcode >> OP_SH_RD) |
| & OP_MASK_RD), |
| MIPS_GR_REG)) |
| || (mips_opts.mips16 |
| && (((prev_pinfo & MIPS16_INSN_WRITE_X) |
| && insn_uses_reg (ip, |
| ((prev_insn.insn_opcode |
| >> MIPS16OP_SH_RX) |
| & MIPS16OP_MASK_RX), |
| MIPS16_REG)) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_Y) |
| && insn_uses_reg (ip, |
| ((prev_insn.insn_opcode |
| >> MIPS16OP_SH_RY) |
| & MIPS16OP_MASK_RY), |
| MIPS16_REG)) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_Z) |
| && insn_uses_reg (ip, |
| ((prev_insn.insn_opcode |
| >> MIPS16OP_SH_RZ) |
| & MIPS16OP_MASK_RZ), |
| MIPS16_REG)) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_T) |
| && insn_uses_reg (ip, TREG, MIPS_GR_REG)) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_31) |
| && insn_uses_reg (ip, RA, MIPS_GR_REG)) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) |
| && insn_uses_reg (ip, |
| MIPS16OP_EXTRACT_REG32R (prev_insn. |
| insn_opcode), |
| MIPS_GR_REG)))) |
| /* If the branch writes a register that the previous |
| instruction sets, we can not swap (we know that |
| branches write only to RD or to $31). */ |
| || (! mips_opts.mips16 |
| && (prev_pinfo & INSN_WRITE_GPR_T) |
| && (((pinfo & INSN_WRITE_GPR_D) |
| && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT) |
| == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD))) |
| || ((pinfo & INSN_WRITE_GPR_31) |
| && (((prev_insn.insn_opcode >> OP_SH_RT) |
| & OP_MASK_RT) |
| == 31)))) |
| || (! mips_opts.mips16 |
| && (prev_pinfo & INSN_WRITE_GPR_D) |
| && (((pinfo & INSN_WRITE_GPR_D) |
| && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD) |
| == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD))) |
| || ((pinfo & INSN_WRITE_GPR_31) |
| && (((prev_insn.insn_opcode >> OP_SH_RD) |
| & OP_MASK_RD) |
| == 31)))) |
| || (mips_opts.mips16 |
| && (pinfo & MIPS16_INSN_WRITE_31) |
| && ((prev_pinfo & MIPS16_INSN_WRITE_31) |
| || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y) |
| && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode) |
| == RA)))) |
| /* If the branch writes a register that the previous |
| instruction reads, we can not swap (we know that |
| branches only write to RD or to $31). */ |
| || (! mips_opts.mips16 |
| && (pinfo & INSN_WRITE_GPR_D) |
| && insn_uses_reg (&prev_insn, |
| ((ip->insn_opcode >> OP_SH_RD) |
| & OP_MASK_RD), |
| MIPS_GR_REG)) |
| || (! mips_opts.mips16 |
| && (pinfo & INSN_WRITE_GPR_31) |
| && insn_uses_reg (&prev_insn, 31, MIPS_GR_REG)) |
| || (mips_opts.mips16 |
| && (pinfo & MIPS16_INSN_WRITE_31) |
| && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG)) |
| /* If we are generating embedded PIC code, the branch |
| might be expanded into a sequence which uses $at, so |
| we can't swap with an instruction which reads it. */ |
| || (mips_pic == EMBEDDED_PIC |
| && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG)) |
| /* If the previous previous instruction has a load |
| delay, and sets a register that the branch reads, we |
| can not swap. */ |
| || (! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| /* Itbl support may require additional care here. */ |
| && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY) |
| || (! gpr_interlocks |
| && (prev_prev_insn.insn_mo->pinfo |
| & INSN_LOAD_MEMORY_DELAY))) |
| && insn_uses_reg (ip, |
| ((prev_prev_insn.insn_opcode >> OP_SH_RT) |
| & OP_MASK_RT), |
| MIPS_GR_REG)) |
| /* If one instruction sets a condition code and the |
| other one uses a condition code, we can not swap. */ |
| || ((pinfo & INSN_READ_COND_CODE) |
| && (prev_pinfo & INSN_WRITE_COND_CODE)) |
| || ((pinfo & INSN_WRITE_COND_CODE) |
| && (prev_pinfo & INSN_READ_COND_CODE)) |
| /* If the previous instruction uses the PC, we can not |
| swap. */ |
| || (mips_opts.mips16 |
| && (prev_pinfo & MIPS16_INSN_READ_PC)) |
| /* If the previous instruction was extended, we can not |
| swap. */ |
| || (mips_opts.mips16 && prev_insn_extended) |
| /* If the previous instruction had a fixup in mips16 |
| mode, we can not swap. This normally means that the |
| previous instruction was a 4 byte branch anyhow. */ |
| || (mips_opts.mips16 && prev_insn_fixp) |
| /* If the previous instruction is a sync, sync.l, or |
| sync.p, we can not swap. */ |
| || (prev_pinfo & INSN_SYNC)) |
| { |
| /* We could do even better for unconditional branches to |
| portions of this object file; we could pick up the |
| instruction at the destination, put it in the delay |
| slot, and bump the destination address. */ |
| emit_nop (); |
| /* Update the previous insn information. */ |
| prev_prev_insn = *ip; |
| prev_insn.insn_mo = &dummy_opcode; |
| } |
| else |
| { |
| /* It looks like we can actually do the swap. */ |
| if (! mips_opts.mips16) |
| { |
| char *prev_f; |
| char temp[4]; |
| |
| prev_f = prev_insn_frag->fr_literal + prev_insn_where; |
| memcpy (temp, prev_f, 4); |
| memcpy (prev_f, f, 4); |
| memcpy (f, temp, 4); |
| if (prev_insn_fixp) |
| { |
| prev_insn_fixp->fx_frag = frag_now; |
| prev_insn_fixp->fx_where = f - frag_now->fr_literal; |
| } |
| if (fixp) |
| { |
| fixp->fx_frag = prev_insn_frag; |
| fixp->fx_where = prev_insn_where; |
| } |
| } |
| else |
| { |
| char *prev_f; |
| char temp[2]; |
| |
| assert (prev_insn_fixp == NULL); |
| prev_f = prev_insn_frag->fr_literal + prev_insn_where; |
| memcpy (temp, prev_f, 2); |
| memcpy (prev_f, f, 2); |
| if (reloc_type != BFD_RELOC_MIPS16_JMP) |
| { |
| assert (reloc_type == BFD_RELOC_UNUSED); |
| memcpy (f, temp, 2); |
| } |
| else |
| { |
| memcpy (f, f + 2, 2); |
| memcpy (f + 2, temp, 2); |
| } |
| if (fixp) |
| { |
| fixp->fx_frag = prev_insn_frag; |
| fixp->fx_where = prev_insn_where; |
| } |
| } |
| |
| /* Update the previous insn information; leave prev_insn |
| unchanged. */ |
| prev_prev_insn = *ip; |
| } |
| prev_insn_is_delay_slot = 1; |
| |
| /* If that was an unconditional branch, forget the previous |
| insn information. */ |
| if (pinfo & INSN_UNCOND_BRANCH_DELAY) |
| { |
| prev_prev_insn.insn_mo = &dummy_opcode; |
| prev_insn.insn_mo = &dummy_opcode; |
| } |
| |
| prev_insn_fixp = NULL; |
| prev_insn_reloc_type = BFD_RELOC_UNUSED; |
| prev_insn_extended = 0; |
| } |
| else if (pinfo & INSN_COND_BRANCH_LIKELY) |
| { |
| /* We don't yet optimize a branch likely. What we should do |
| is look at the target, copy the instruction found there |
| into the delay slot, and increment the branch to jump to |
| the next instruction. */ |
| emit_nop (); |
| /* Update the previous insn information. */ |
| prev_prev_insn = *ip; |
| prev_insn.insn_mo = &dummy_opcode; |
| prev_insn_fixp = NULL; |
| prev_insn_reloc_type = BFD_RELOC_UNUSED; |
| prev_insn_extended = 0; |
| } |
| else |
| { |
| /* Update the previous insn information. */ |
| if (nops > 0) |
| prev_prev_insn.insn_mo = &dummy_opcode; |
| else |
| prev_prev_insn = prev_insn; |
| prev_insn = *ip; |
| |
| /* Any time we see a branch, we always fill the delay slot |
| immediately; since this insn is not a branch, we know it |
| is not in a delay slot. */ |
| prev_insn_is_delay_slot = 0; |
| |
| prev_insn_fixp = fixp; |
| prev_insn_reloc_type = reloc_type; |
| if (mips_opts.mips16) |
| prev_insn_extended = (ip->use_extend |
| || reloc_type > BFD_RELOC_UNUSED); |
| } |
| |
| prev_prev_insn_unreordered = prev_insn_unreordered; |
| prev_insn_unreordered = 0; |
| prev_insn_frag = frag_now; |
| prev_insn_where = f - frag_now->fr_literal; |
| prev_insn_valid = 1; |
| } |
| else if (place == NULL) |
| { |
| /* We need to record a bit of information even when we are not |
| reordering, in order to determine the base address for mips16 |
| PC relative relocs. */ |
| prev_prev_insn = prev_insn; |
| prev_insn = *ip; |
| prev_insn_reloc_type = reloc_type; |
| prev_prev_insn_unreordered = prev_insn_unreordered; |
| prev_insn_unreordered = 1; |
| } |
| |
| /* We just output an insn, so the next one doesn't have a label. */ |
| mips_clear_insn_labels (); |
| |
| /* We must ensure that a fixup associated with an unmatched %hi |
| reloc does not become a variant frag. Otherwise, the |
| rearrangement of %hi relocs in frob_file may confuse |
| tc_gen_reloc. */ |
| if (unmatched_hi) |
| { |
| frag_wane (frag_now); |
| frag_new (0); |
| } |
| } |
| |
| /* This function forgets that there was any previous instruction or |
| label. If PRESERVE is non-zero, it remembers enough information to |
| know whether nops are needed before a noreorder section. */ |
| |
| static void |
| mips_no_prev_insn (preserve) |
| int preserve; |
| { |
| if (! preserve) |
| { |
| prev_insn.insn_mo = &dummy_opcode; |
| prev_prev_insn.insn_mo = &dummy_opcode; |
| prev_nop_frag = NULL; |
| prev_nop_frag_holds = 0; |
| prev_nop_frag_required = 0; |
| prev_nop_frag_since = 0; |
| } |
| prev_insn_valid = 0; |
| prev_insn_is_delay_slot = 0; |
| prev_insn_unreordered = 0; |
| prev_insn_extended = 0; |
| prev_insn_reloc_type = BFD_RELOC_UNUSED; |
| prev_prev_insn_unreordered = 0; |
| mips_clear_insn_labels (); |
| } |
| |
| /* This function must be called whenever we turn on noreorder or emit |
| something other than instructions. It inserts any NOPS which might |
| be needed by the previous instruction, and clears the information |
| kept for the previous instructions. The INSNS parameter is true if |
| instructions are to follow. */ |
| |
| static void |
| mips_emit_delays (insns) |
| boolean insns; |
| { |
| if (! mips_opts.noreorder) |
| { |
| int nops; |
| |
| nops = 0; |
| if ((! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (! cop_interlocks |
| && (prev_insn.insn_mo->pinfo |
| & (INSN_LOAD_COPROC_DELAY |
| | INSN_COPROC_MOVE_DELAY |
| | INSN_WRITE_COND_CODE)))) |
| || (! hilo_interlocks |
| && (prev_insn.insn_mo->pinfo |
| & (INSN_READ_LO |
| | INSN_READ_HI))) |
| || (! mips_opts.mips16 |
| && ! gpr_interlocks |
| && (prev_insn.insn_mo->pinfo |
| & INSN_LOAD_MEMORY_DELAY)) |
| || (! mips_opts.mips16 |
| && mips_opts.isa == ISA_MIPS1 |
| && (prev_insn.insn_mo->pinfo |
| & INSN_COPROC_MEMORY_DELAY))) |
| { |
| /* Itbl support may require additional care here. */ |
| ++nops; |
| if ((! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (! cop_interlocks |
| && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)) |
| || (! hilo_interlocks |
| && ((prev_insn.insn_mo->pinfo & INSN_READ_HI) |
| || (prev_insn.insn_mo->pinfo & INSN_READ_LO)))) |
| ++nops; |
| |
| if (prev_insn_unreordered) |
| nops = 0; |
| } |
| else if ((! mips_opts.mips16 |
| && ISA_HAS_COPROC_DELAYS (mips_opts.isa) |
| && (! cop_interlocks |
| && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)) |
| || (! hilo_interlocks |
| && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) |
| || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)))) |
| { |
| /* Itbl support may require additional care here. */ |
| if (! prev_prev_insn_unreordered) |
| ++nops; |
| } |
| |
| if (nops > 0) |
| { |
| struct insn_label_list *l; |
| |
| if (insns) |
| { |
| /* Record the frag which holds the nop instructions, so |
| that we can remove them if we don't need them. */ |
| frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4); |
| prev_nop_frag = frag_now; |
| prev_nop_frag_holds = nops; |
| prev_nop_frag_required = 0; |
| prev_nop_frag_since = 0; |
| } |
| |
| for (; nops > 0; --nops) |
| emit_nop (); |
| |
| if (insns) |
| { |
| /* Move on to a new frag, so that it is safe to simply |
| decrease the size of prev_nop_frag. */ |
| frag_wane (frag_now); |
| frag_new (0); |
| } |
| |
| for (l = insn_labels; l != NULL; l = l->next) |
| { |
| valueT val; |
| |
| assert (S_GET_SEGMENT (l->label) == now_seg); |
| symbol_set_frag (l->label, frag_now); |
| val = (valueT) frag_now_fix (); |
| /* mips16 text labels are stored as odd. */ |
| if (mips_opts.mips16) |
| val += 1; |
| S_SET_VALUE (l->label, val); |
| } |
| } |
| } |
| |
| /* Mark instruction labels in mips16 mode. */ |
| if (mips_opts.mips16 && insns) |
| mips16_mark_labels (); |
| |
| mips_no_prev_insn (insns); |
| } |
| |
| /* 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. */ |
| |
| #ifdef USE_STDARG |
| static void |
| macro_build (char *place, |
| int *counter, |
| expressionS * ep, |
| const char *name, |
| const char *fmt, |
| ...) |
| #else |
| static void |
| macro_build (place, counter, ep, name, fmt, va_alist) |
| char *place; |
| int *counter; |
| expressionS *ep; |
| const char *name; |
| const char *fmt; |
| va_dcl |
| #endif |
| { |
| struct mips_cl_insn insn; |
| bfd_reloc_code_real_type r; |
| va_list args; |
| |
| #ifdef USE_STDARG |
| va_start (args, fmt); |
| #else |
| va_start (args); |
| #endif |
| |
| /* |
| * If the macro is about to expand into a second instruction, |
| * print a warning if needed. We need to pass ip as a parameter |
| * to generate a better warning message here... |
| */ |
| if (mips_opts.warn_about_macros && place == NULL && *counter == 1) |
| as_warn (_("Macro instruction expanded into multiple instructions")); |
| |
| if (place == NULL) |
| *counter += 1; /* bump instruction counter */ |
| |
| if (mips_opts.mips16) |
| { |
| mips16_macro_build (place, counter, ep, name, fmt, args); |
| va_end (args); |
| return; |
| } |
| |
| r = BFD_RELOC_UNUSED; |
| insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name); |
| assert (insn.insn_mo); |
| assert (strcmp (name, insn.insn_mo->name) == 0); |
| |
| /* Search until we get a match for NAME. */ |
| while (1) |
| { |
| if (strcmp (fmt, insn.insn_mo->args) == 0 |
| && insn.insn_mo->pinfo != INSN_MACRO |
| && OPCODE_IS_MEMBER (insn.insn_mo, mips_opts.isa, mips_cpu, |
| mips_gp32) |
| && (mips_cpu != CPU_R4650 || (insn.insn_mo->pinfo & FP_D) == 0)) |
| break; |
| |
| ++insn.insn_mo; |
| assert (insn.insn_mo->name); |
| assert (strcmp (name, insn.insn_mo->name) == 0); |
| } |
| |
| insn.insn_opcode = insn.insn_mo->match; |
| for (;;) |
| { |
| switch (*fmt++) |
| { |
| case '\0': |
| break; |
| |
| case ',': |
| case '(': |
| case ')': |
| continue; |
| |
| case 't': |
| case 'w': |
| case 'E': |
| insn.insn_opcode |= va_arg (args, int) << 16; |
| continue; |
| |
| case 'c': |
| case 'T': |
| case 'W': |
| insn.insn_opcode |= va_arg (args, int) << 16; |
| continue; |
| |
| case 'd': |
| case 'G': |
| insn.insn_opcode |= va_arg (args, int) << 11; |
| continue; |
| |
| case 'U': |
| { |
| int tmp = va_arg (args, int); |
| |
| insn.insn_opcode |= tmp << 16; |
| insn.insn_opcode |= tmp << 11; |
| continue; |
| } |
| |
| case 'V': |
| case 'S': |
| insn.insn_opcode |= va_arg (args, int) << 11; |
| continue; |
| |
| case 'z': |
| continue; |
| |
| case '<': |
| insn.insn_opcode |= va_arg (args, int) << 6; |
| continue; |
| |
| case 'D': |
| insn.insn_opcode |= va_arg (args, int) << 6; |
| continue; |
| |
| case 'B': |
| insn.insn_opcode |= va_arg (args, int) << 6; |
| continue; |
| |
| case 'J': |
| insn.insn_opcode |= va_arg (args, int) << 6; |
| continue; |
| |
| case 'q': |
| insn.insn_opcode |= va_arg (args, int) << 6; |
| continue; |
| |
| case 'b': |
| case 's': |
| case 'r': |
| case 'v': |
| insn.insn_opcode |= va_arg (args, int) << 21; |
| continue; |
| |
| case 'i': |
| case 'j': |
| case 'o': |
| r = (bfd_reloc_code_real_type) va_arg (args, int); |
| assert (r == BFD_RELOC_MIPS_GPREL |
| || r == BFD_RELOC_MIPS_LITERAL |
| || r == BFD_RELOC_LO16 |
| || r == BFD_RELOC_MIPS_GOT16 |
| || r == BFD_RELOC_MIPS_CALL16 |
| || r == BFD_RELOC_MIPS_GOT_LO16 |
| || r == BFD_RELOC_MIPS_CALL_LO16 |
| || (ep->X_op == O_subtract |
| && r == BFD_RELOC_PCREL_LO16)); |
| continue; |
| |
| case 'u': |
| r = (bfd_reloc_code_real_type) va_arg (args, int); |
| assert (ep != NULL |
| && (ep->X_op == O_constant |
| || (ep->X_op == O_symbol |
| && (r == BFD_RELOC_HI16_S |
| || r == BFD_RELOC_HI16 |
| || r == BFD_RELOC_MIPS_GOT_HI16 |
| || r == BFD_RELOC_MIPS_CALL_HI16)) |
| || (ep->X_op == O_subtract |
| && r == BFD_RELOC_PCREL_HI16_S))); |
| if (ep->X_op == O_constant) |
| { |
| insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff; |
| ep = NULL; |
| r = BFD_RELOC_UNUSED; |
| } |
| continue; |
| |
| case 'p': |
| assert (ep != NULL); |
| /* |
| * This allows macro() to pass an immediate expression for |
| * creating short branches without creating a symbol. |
| * Note that the expression still might come from the assembly |
| * input, in which case the value is not checked for range nor |
| * is a relocation entry generated (yuck). |
| */ |
| if (ep->X_op == O_constant) |
| { |
| insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff; |
| ep = NULL; |
| } |
| else |
| r = BFD_RELOC_16_PCREL_S2; |
| continue; |
| |
| case 'a': |
| assert (ep != NULL); |
| r = BFD_RELOC_MIPS_JMP; |
| continue; |
| |
| case 'C': |
| insn.insn_opcode |= va_arg (args, unsigned long); |
| continue; |
| |
| default: |
| internalError (); |
| } |
| break; |
| } |
| va_end (args); |
| assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); |
| |
| append_insn (place, &insn, ep, r, false); |
| } |
| |
| static void |
| mips16_macro_build (place, counter, ep, name, fmt, args) |
| char *place; |
| int *counter ATTRIBUTE_UNUSED; |
| expressionS *ep; |
| const char *name; |
| const char *fmt; |
| va_list args; |
| { |
| struct mips_cl_insn insn; |
| bfd_reloc_code_real_type r; |
| |
| r = BFD_RELOC_UNUSED; |
| insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name); |
| assert (insn.insn_mo); |
| assert (strcmp (name, insn.insn_mo->name) == 0); |
| |
| while (strcmp (fmt, insn.insn_mo->args) != 0 |
| || insn.insn_mo->pinfo == INSN_MACRO) |
| { |
| ++insn.insn_mo; |
| assert (insn.insn_mo->name); |
| assert (strcmp (name, insn.insn_mo->name) == 0); |
| } |
| |
| insn.insn_opcode = insn.insn_mo->match; |
| insn.use_extend = false; |
| |
| for (;;) |
| { |
| int c; |
| |
| c = *fmt++; |
| switch (c) |
| { |
| case '\0': |
| break; |
| |
| case ',': |
| case '(': |
| case ')': |
| continue; |
| |
| case 'y': |
| case 'w': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY; |
| continue; |
| |
| case 'x': |
| case 'v': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX; |
| continue; |
| |
| case 'z': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ; |
| continue; |
| |
| case 'Z': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z; |
| continue; |
| |
| case '0': |
| case 'S': |
| case 'P': |
| case 'R': |
| continue; |
| |
| case 'X': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32; |
| continue; |
| |
| case 'Y': |
| { |
| int regno; |
| |
| regno = va_arg (args, int); |
| regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); |
| insn.insn_opcode |= regno << MIPS16OP_SH_REG32R; |
| } |
| continue; |
| |
| case '<': |
| case '>': |
| case '4': |
| case '5': |
| case 'H': |
| case 'W': |
| case 'D': |
| case 'j': |
| case '8': |
| case 'V': |
| case 'C': |
| case 'U': |
| case 'k': |
| case 'K': |
| case 'p': |
| case 'q': |
| { |
| assert (ep != NULL); |
| |
| if (ep->X_op != O_constant) |
| r = BFD_RELOC_UNUSED + c; |
| else |
| { |
| mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false, |
| false, false, &insn.insn_opcode, |
| &insn.use_extend, &insn.extend); |
| ep = NULL; |
| r = BFD_RELOC_UNUSED; |
| } |
| } |
| continue; |
| |
| case '6': |
| insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6; |
| continue; |
| } |
| |
| break; |
| } |
| |
| assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); |
| |
| append_insn (place, &insn, ep, r, false); |
| } |
| |
| /* |
| * Generate a "lui" instruction. |
| */ |
| static void |
| macro_build_lui (place, counter, ep, regnum) |
| char *place; |
| int *counter; |
| expressionS *ep; |
| int regnum; |
| { |
| expressionS high_expr; |
| struct mips_cl_insn insn; |
| bfd_reloc_code_real_type r; |
| CONST char *name = "lui"; |
| CONST char *fmt = "t,u"; |
| |
| assert (! mips_opts.mips16); |
| |
| if (place == NULL) |
| high_expr = *ep; |
| else |
| { |
| high_expr.X_op = O_constant; |
| high_expr.X_add_number = ep->X_add_number; |
| } |
| |
| if (high_expr.X_op == O_constant) |
| { |
| /* we can compute the instruction now without a relocation entry */ |
| if (high_expr.X_add_number & 0x8000) |
| high_expr.X_add_number += 0x10000; |
| high_expr.X_add_number = |
| ((unsigned long) high_expr.X_add_number >> 16) & 0xffff; |
| r = BFD_RELOC_UNUSED; |
| } |
| else |
| { |
| assert (ep->X_op == O_symbol); |
| /* _gp_disp is a special case, used from s_cpload. */ |
| assert (mips_pic == NO_PIC |
| || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0); |
| r = BFD_RELOC_HI16_S; |
| } |
| |
| /* |
| * If the macro is about to expand into a second instruction, |
|