blob: 1148b435a25d5aa1f0365026d33fd9186d179eae [file] [log] [blame]
/* 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,