Line 89... |
Line 89... |
static char *mips_regmask_frag;
|
static char *mips_regmask_frag;
|
#endif
|
#endif
|
|
|
#define ZERO 0
|
#define ZERO 0
|
#define ATREG 1
|
#define ATREG 1
|
|
#define S0 16
|
|
#define S7 23
|
#define TREG 24
|
#define TREG 24
|
#define PIC_CALL_REG 25
|
#define PIC_CALL_REG 25
|
#define KT0 26
|
#define KT0 26
|
#define KT1 27
|
#define KT1 27
|
#define GP 28
|
#define GP 28
|
Line 119... |
Line 121... |
? ".rdata" \
|
? ".rdata" \
|
: OUTPUT_FLAVOR == bfd_target_elf_flavour \
|
: OUTPUT_FLAVOR == bfd_target_elf_flavour \
|
? ".rodata" \
|
? ".rodata" \
|
: (abort (), ""))
|
: (abort (), ""))
|
|
|
|
/* Ways in which an instruction can be "appended" to the output. */
|
|
enum append_method {
|
|
/* Just add it normally. */
|
|
APPEND_ADD,
|
|
|
|
/* Add it normally and then add a nop. */
|
|
APPEND_ADD_WITH_NOP,
|
|
|
|
/* Turn an instruction with a delay slot into a "compact" version. */
|
|
APPEND_ADD_COMPACT,
|
|
|
|
/* Insert the instruction before the last one. */
|
|
APPEND_SWAP
|
|
};
|
|
|
/* Information about an instruction, including its format, operands
|
/* Information about an instruction, including its format, operands
|
and fixups. */
|
and fixups. */
|
struct mips_cl_insn
|
struct mips_cl_insn
|
{
|
{
|
/* The opcode's entry in mips_opcodes or mips16_opcodes. */
|
/* The opcode's entry in mips_opcodes or mips16_opcodes. */
|
Line 199... |
Line 216... |
int ase_mdmx;
|
int ase_mdmx;
|
int ase_smartmips;
|
int ase_smartmips;
|
int ase_dsp;
|
int ase_dsp;
|
int ase_dspr2;
|
int ase_dspr2;
|
int ase_mt;
|
int ase_mt;
|
|
int ase_mcu;
|
/* Whether we are assembling for the mips16 processor. 0 if we are
|
/* 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.
|
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
|
Changed by `.set mips16' and `.set nomips16', and the -mips16 and
|
-nomips16 command line options, and the default CPU. */
|
-nomips16 command line options, and the default CPU. */
|
int mips16;
|
int mips16;
|
|
/* Whether we are assembling for the mipsMIPS ASE. 0 if we are not,
|
|
1 if we are, and -1 if the value has not been initialized. Changed
|
|
by `.set micromips' and `.set nomicromips', and the -mmicromips
|
|
and -mno-micromips command line options, and the default CPU. */
|
|
int micromips;
|
/* Non-zero if we should not reorder instructions. Changed by `.set
|
/* Non-zero if we should not reorder instructions. Changed by `.set
|
reorder' and `.set noreorder'. */
|
reorder' and `.set noreorder'. */
|
int noreorder;
|
int noreorder;
|
/* Non-zero if we should not permit the register designated "assembler
|
/* Non-zero if we should not permit the register designated "assembler
|
temporary" to be used in instructions. The value is the register
|
temporary" to be used in instructions. The value is the register
|
Line 268... |
Line 291... |
|
|
static struct mips_set_options mips_opts =
|
static struct mips_set_options mips_opts =
|
{
|
{
|
/* isa */ ISA_UNKNOWN, /* ase_mips3d */ -1, /* ase_mdmx */ -1,
|
/* isa */ ISA_UNKNOWN, /* ase_mips3d */ -1, /* ase_mdmx */ -1,
|
/* ase_smartmips */ 0, /* ase_dsp */ -1, /* ase_dspr2 */ -1, /* ase_mt */ -1,
|
/* ase_smartmips */ 0, /* ase_dsp */ -1, /* ase_dspr2 */ -1, /* ase_mt */ -1,
|
/* mips16 */ -1, /* noreorder */ 0, /* at */ ATREG,
|
/* ase_mcu */ -1, /* mips16 */ -1, /* micromips */ -1, /* noreorder */ 0,
|
/* warn_about_macros */ 0, /* nomove */ 0, /* nobopt */ 0,
|
/* at */ ATREG, /* warn_about_macros */ 0, /* nomove */ 0, /* nobopt */ 0,
|
/* noautoextend */ 0, /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN,
|
/* noautoextend */ 0, /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN,
|
/* sym32 */ FALSE, /* soft_float */ FALSE, /* single_float */ FALSE
|
/* sym32 */ FALSE, /* soft_float */ FALSE, /* single_float */ FALSE
|
};
|
};
|
|
|
/* These variables are filled in with the masks of registers used.
|
/* These variables are filled in with the masks of registers used.
|
Line 291... |
Line 314... |
#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \
|
#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \
|
|| mips_opts.isa == ISA_MIPS32R2 \
|
|| mips_opts.isa == ISA_MIPS32R2 \
|
|| mips_opts.isa == ISA_MIPS64 \
|
|| mips_opts.isa == ISA_MIPS64 \
|
|| mips_opts.isa == ISA_MIPS64R2)
|
|| mips_opts.isa == ISA_MIPS64R2)
|
|
|
|
/* True if any microMIPS code was produced. */
|
|
static int file_ase_micromips;
|
|
|
/* True if we want to create R_MIPS_JALR for jalr $25. */
|
/* True if we want to create R_MIPS_JALR for jalr $25. */
|
#ifdef TE_IRIX
|
#ifdef TE_IRIX
|
#define MIPS_JALR_HINT_P(EXPR) HAVE_NEWABI
|
#define MIPS_JALR_HINT_P(EXPR) HAVE_NEWABI
|
#else
|
#else
|
/* As a GNU extension, we use R_MIPS_JALR for o32 too. However,
|
/* As a GNU extension, we use R_MIPS_JALR for o32 too. However,
|
Line 341... |
Line 367... |
static int file_ase_mt;
|
static int file_ase_mt;
|
|
|
#define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2 \
|
#define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2 \
|
|| mips_opts.isa == ISA_MIPS64R2)
|
|| mips_opts.isa == ISA_MIPS64R2)
|
|
|
|
#define ISA_SUPPORTS_MCU_ASE (mips_opts.isa == ISA_MIPS32R2 \
|
|
|| mips_opts.isa == ISA_MIPS64R2)
|
|
|
/* The argument of the -march= flag. The architecture we are assembling. */
|
/* The argument of the -march= flag. The architecture we are assembling. */
|
static int file_mips_arch = CPU_UNKNOWN;
|
static int file_mips_arch = CPU_UNKNOWN;
|
static const char *mips_arch_string;
|
static const char *mips_arch_string;
|
|
|
/* The argument of the -mtune= flag. The architecture for which we
|
/* The argument of the -mtune= flag. The architecture for which we
|
Line 382... |
Line 411... |
|| (ISA) == ISA_MIPS64R2)
|
|| (ISA) == ISA_MIPS64R2)
|
|
|
/* Return true if ISA supports 64-bit right rotate (dror et al.)
|
/* Return true if ISA supports 64-bit right rotate (dror et al.)
|
instructions. */
|
instructions. */
|
#define ISA_HAS_DROR(ISA) \
|
#define ISA_HAS_DROR(ISA) \
|
((ISA) == ISA_MIPS64R2)
|
((ISA) == ISA_MIPS64R2 \
|
|
|| (mips_opts.micromips \
|
|
&& ISA_HAS_64BIT_REGS (ISA)) \
|
|
)
|
|
|
/* Return true if ISA supports 32-bit right rotate (ror et al.)
|
/* Return true if ISA supports 32-bit right rotate (ror et al.)
|
instructions. */
|
instructions. */
|
#define ISA_HAS_ROR(ISA) \
|
#define ISA_HAS_ROR(ISA) \
|
((ISA) == ISA_MIPS32R2 \
|
((ISA) == ISA_MIPS32R2 \
|
|| (ISA) == ISA_MIPS64R2 \
|
|| (ISA) == ISA_MIPS64R2 \
|
|| mips_opts.ase_smartmips)
|
|| mips_opts.ase_smartmips \
|
|
|| mips_opts.micromips \
|
|
)
|
|
|
/* Return true if ISA supports single-precision floats in odd registers. */
|
/* Return true if ISA supports single-precision floats in odd registers. */
|
#define ISA_HAS_ODD_SINGLE_FPR(ISA) \
|
#define ISA_HAS_ODD_SINGLE_FPR(ISA) \
|
((ISA) == ISA_MIPS32 \
|
((ISA) == ISA_MIPS32 \
|
|| (ISA) == ISA_MIPS32R2 \
|
|| (ISA) == ISA_MIPS32R2 \
|
Line 451... |
Line 485... |
/* Return true if the given CPU supports the MIPS16 ASE. */
|
/* Return true if the given CPU supports the MIPS16 ASE. */
|
#define CPU_HAS_MIPS16(cpu) \
|
#define CPU_HAS_MIPS16(cpu) \
|
(strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \
|
(strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \
|
|| strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
|
|| strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
|
|
|
|
/* Return true if the given CPU supports the microMIPS ASE. */
|
|
#define CPU_HAS_MICROMIPS(cpu) 0
|
|
|
/* True if CPU has a dror instruction. */
|
/* True if CPU has a dror instruction. */
|
#define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
|
#define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
|
|
|
/* True if CPU has a ror instruction. */
|
/* True if CPU has a ror instruction. */
|
#define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU)
|
#define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU)
|
Line 489... |
Line 526... |
|| mips_opts.arch == CPU_R12000 \
|
|| mips_opts.arch == CPU_R12000 \
|
|| mips_opts.arch == CPU_R14000 \
|
|| mips_opts.arch == CPU_R14000 \
|
|| mips_opts.arch == CPU_R16000 \
|
|| mips_opts.arch == CPU_R16000 \
|
|| mips_opts.arch == CPU_RM7000 \
|
|| mips_opts.arch == CPU_RM7000 \
|
|| mips_opts.arch == CPU_VR5500 \
|
|| mips_opts.arch == CPU_VR5500 \
|
|
|| mips_opts.micromips \
|
)
|
)
|
|
|
/* Whether the processor uses hardware interlocks to protect reads
|
/* Whether the processor uses hardware interlocks to protect reads
|
from the GPRs after they are loaded from memory, and thus does not
|
from the GPRs after they are loaded from memory, and thus does not
|
require nops to be inserted. This applies to instructions marked
|
require nops to be inserted. This applies to instructions marked
|
INSN_LOAD_MEMORY_DELAY. These nops are only required at MIPS ISA
|
INSN_LOAD_MEMORY_DELAY. These nops are only required at MIPS ISA
|
level I. */
|
level I and microMIPS mode instructions are always interlocked. */
|
#define gpr_interlocks \
|
#define gpr_interlocks \
|
(mips_opts.isa != ISA_MIPS1 \
|
(mips_opts.isa != ISA_MIPS1 \
|
|| mips_opts.arch == CPU_R3900)
|
|| mips_opts.arch == CPU_R3900 \
|
|
|| mips_opts.micromips \
|
|
)
|
|
|
/* Whether the processor uses hardware interlocks to avoid delays
|
/* Whether the processor uses hardware interlocks to avoid delays
|
required by coprocessor instructions, and thus does not require
|
required by coprocessor instructions, and thus does not require
|
nops to be inserted. This applies to instructions marked
|
nops to be inserted. This applies to instructions marked
|
INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
|
INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
|
between instructions marked INSN_WRITE_COND_CODE and ones marked
|
between instructions marked INSN_WRITE_COND_CODE and ones marked
|
INSN_READ_COND_CODE. These nops are only required at MIPS ISA
|
INSN_READ_COND_CODE. These nops are only required at MIPS ISA
|
levels I, II, and III. */
|
levels I, II, and III and microMIPS mode instructions are always
|
|
interlocked. */
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
#define cop_interlocks \
|
#define cop_interlocks \
|
((mips_opts.isa != ISA_MIPS1 \
|
((mips_opts.isa != ISA_MIPS1 \
|
&& mips_opts.isa != ISA_MIPS2 \
|
&& mips_opts.isa != ISA_MIPS2 \
|
&& mips_opts.isa != ISA_MIPS3) \
|
&& mips_opts.isa != ISA_MIPS3) \
|
|| mips_opts.arch == CPU_R4300 \
|
|| mips_opts.arch == CPU_R4300 \
|
|
|| mips_opts.micromips \
|
)
|
)
|
|
|
/* Whether the processor uses hardware interlocks to protect reads
|
/* Whether the processor uses hardware interlocks to protect reads
|
from coprocessor registers after they are loaded from memory, and
|
from coprocessor registers after they are loaded from memory, and
|
thus does not require nops to be inserted. This applies to
|
thus does not require nops to be inserted. This applies to
|
instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only
|
instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only
|
requires at MIPS ISA level I. */
|
requires at MIPS ISA level I and microMIPS mode instructions are
|
#define cop_mem_interlocks (mips_opts.isa != ISA_MIPS1)
|
always interlocked. */
|
|
#define cop_mem_interlocks \
|
|
(mips_opts.isa != ISA_MIPS1 \
|
|
|| mips_opts.micromips \
|
|
)
|
|
|
/* Is this a mfhi or mflo instruction? */
|
/* Is this a mfhi or mflo instruction? */
|
#define MF_HILO_INSN(PINFO) \
|
#define MF_HILO_INSN(PINFO) \
|
((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
|
((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
|
|
|
Line 535... |
Line 581... |
#define COP_INSN(PINFO) \
|
#define COP_INSN(PINFO) \
|
(PINFO != INSN_MACRO \
|
(PINFO != INSN_MACRO \
|
&& ((PINFO) & (FP_S | FP_D)) == 0 \
|
&& ((PINFO) & (FP_S | FP_D)) == 0 \
|
&& ((PINFO) & (INSN_COP | INSN_READ_COND_CODE | INSN_WRITE_COND_CODE)))
|
&& ((PINFO) & (INSN_COP | INSN_READ_COND_CODE | INSN_WRITE_COND_CODE)))
|
|
|
|
/* Whether code compression (either of the MIPS16 or the microMIPS ASEs)
|
|
has been selected. This implies, in particular, that addresses of text
|
|
labels have their LSB set. */
|
|
#define HAVE_CODE_COMPRESSION \
|
|
((mips_opts.mips16 | mips_opts.micromips) != 0)
|
|
|
/* MIPS PIC level. */
|
/* MIPS PIC level. */
|
|
|
enum mips_pic_level mips_pic;
|
enum mips_pic_level mips_pic;
|
|
|
/* 1 if we should generate 32 bit offsets from the $gp register in
|
/* 1 if we should generate 32 bit offsets from the $gp register in
|
Line 591... |
Line 643... |
static struct hash_control *op_hash = NULL;
|
static struct hash_control *op_hash = NULL;
|
|
|
/* The opcode hash table we use for the mips16. */
|
/* The opcode hash table we use for the mips16. */
|
static struct hash_control *mips16_op_hash = NULL;
|
static struct hash_control *mips16_op_hash = NULL;
|
|
|
|
/* The opcode hash table we use for the microMIPS ASE. */
|
|
static struct hash_control *micromips_op_hash = NULL;
|
|
|
/* This array holds the chars that always start a comment. If the
|
/* This array holds the chars that always start a comment. If the
|
pre-processor is disabled, these aren't very useful */
|
pre-processor is disabled, these aren't very useful */
|
const char comment_chars[] = "#";
|
const char comment_chars[] = "#";
|
|
|
/* This array holds the chars that only start a comment at the beginning of
|
/* This array holds the chars that only start a comment at the beginning of
|
Line 680... |
Line 735... |
instruction further if we're thinking about using history[0] to
|
instruction further if we're thinking about using history[0] to
|
fill a branch delay slot. */
|
fill a branch delay slot. */
|
static struct mips_cl_insn history[1 + MAX_NOPS];
|
static struct mips_cl_insn history[1 + MAX_NOPS];
|
|
|
/* Nop instructions used by emit_nop. */
|
/* Nop instructions used by emit_nop. */
|
static struct mips_cl_insn nop_insn, mips16_nop_insn;
|
static struct mips_cl_insn nop_insn;
|
|
static struct mips_cl_insn mips16_nop_insn;
|
|
static struct mips_cl_insn micromips_nop16_insn;
|
|
static struct mips_cl_insn micromips_nop32_insn;
|
|
|
/* The appropriate nop for the current mode. */
|
/* The appropriate nop for the current mode. */
|
#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn : &nop_insn)
|
#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn \
|
|
: (mips_opts.micromips ? µmips_nop16_insn : &nop_insn))
|
|
|
|
/* The size of NOP_INSN in bytes. */
|
|
#define NOP_INSN_SIZE (HAVE_CODE_COMPRESSION ? 2 : 4)
|
|
|
/* If this is set, it points to a frag holding nop instructions which
|
/* If this is set, it points to a frag holding nop instructions which
|
were inserted before the start of a noreorder section. If those
|
were inserted before the start of a noreorder section. If those
|
nops turn out to be unnecessary, the size of the frag can be
|
nops turn out to be unnecessary, the size of the frag can be
|
decreased. */
|
decreased. */
|
Line 750... |
Line 812... |
static const unsigned int mips16_to_32_reg_map[] =
|
static const unsigned int mips16_to_32_reg_map[] =
|
{
|
{
|
16, 17, 2, 3, 4, 5, 6, 7
|
16, 17, 2, 3, 4, 5, 6, 7
|
};
|
};
|
|
|
|
/* Map normal MIPS register numbers to microMIPS register numbers. */
|
|
|
|
#define mips32_to_micromips_reg_b_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_c_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_d_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_e_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_f_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_g_map mips32_to_16_reg_map
|
|
#define mips32_to_micromips_reg_l_map mips32_to_16_reg_map
|
|
|
|
#define X ILLEGAL_REG
|
|
/* reg type h: 4, 5, 6. */
|
|
static const int mips32_to_micromips_reg_h_map[] =
|
|
{
|
|
X, X, X, X, 4, 5, 6, X,
|
|
X, X, X, X, X, X, X, X,
|
|
X, X, X, X, X, X, X, X,
|
|
X, X, X, X, X, X, X, X
|
|
};
|
|
|
|
/* reg type m: 0, 17, 2, 3, 16, 18, 19, 20. */
|
|
static const int mips32_to_micromips_reg_m_map[] =
|
|
{
|
|
0, X, 2, 3, X, X, X, X,
|
|
X, X, X, X, X, X, X, X,
|
|
4, 1, 5, 6, 7, X, X, X,
|
|
X, X, X, X, X, X, X, X
|
|
};
|
|
|
|
/* reg type q: 0, 2-7. 17. */
|
|
static const int mips32_to_micromips_reg_q_map[] =
|
|
{
|
|
0, X, 2, 3, 4, 5, 6, 7,
|
|
X, X, X, X, X, X, X, X,
|
|
X, 1, X, X, X, X, X, X,
|
|
X, X, X, X, X, X, X, X
|
|
};
|
|
|
|
#define mips32_to_micromips_reg_n_map mips32_to_micromips_reg_m_map
|
|
#undef X
|
|
|
|
/* Map microMIPS register numbers to normal MIPS register numbers. */
|
|
|
|
#define micromips_to_32_reg_b_map mips16_to_32_reg_map
|
|
#define micromips_to_32_reg_c_map mips16_to_32_reg_map
|
|
#define micromips_to_32_reg_d_map mips16_to_32_reg_map
|
|
#define micromips_to_32_reg_e_map mips16_to_32_reg_map
|
|
#define micromips_to_32_reg_f_map mips16_to_32_reg_map
|
|
#define micromips_to_32_reg_g_map mips16_to_32_reg_map
|
|
|
|
/* The microMIPS registers with type h. */
|
|
static const unsigned int micromips_to_32_reg_h_map[] =
|
|
{
|
|
5, 5, 6, 4, 4, 4, 4, 4
|
|
};
|
|
|
|
/* The microMIPS registers with type i. */
|
|
static const unsigned int micromips_to_32_reg_i_map[] =
|
|
{
|
|
6, 7, 7, 21, 22, 5, 6, 7
|
|
};
|
|
|
|
#define micromips_to_32_reg_l_map mips16_to_32_reg_map
|
|
|
|
/* The microMIPS registers with type m. */
|
|
static const unsigned int micromips_to_32_reg_m_map[] =
|
|
{
|
|
0, 17, 2, 3, 16, 18, 19, 20
|
|
};
|
|
|
|
#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map
|
|
|
|
/* The microMIPS registers with type q. */
|
|
static const unsigned int micromips_to_32_reg_q_map[] =
|
|
{
|
|
0, 17, 2, 3, 4, 5, 6, 7
|
|
};
|
|
|
|
/* microMIPS imm type B. */
|
|
static const int micromips_imm_b_map[] =
|
|
{
|
|
1, 4, 8, 12, 16, 20, 24, -1
|
|
};
|
|
|
|
/* microMIPS imm type C. */
|
|
static const int micromips_imm_c_map[] =
|
|
{
|
|
128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
|
|
};
|
|
|
/* Classifies the kind of instructions we're interested in when
|
/* Classifies the kind of instructions we're interested in when
|
implementing -mfix-vr4120. */
|
implementing -mfix-vr4120. */
|
enum fix_vr4120_class
|
enum fix_vr4120_class
|
{
|
{
|
FIX_VR4120_MACC,
|
FIX_VR4120_MACC,
|
Line 831... |
Line 983... |
|
|
RELAX_DELAY_SLOT
|
RELAX_DELAY_SLOT
|
Like RELAX_NOMACRO, but indicates that the macro appears in a branch
|
Like RELAX_NOMACRO, but indicates that the macro appears in a branch
|
delay slot.
|
delay slot.
|
|
|
|
RELAX_DELAY_SLOT_16BIT
|
|
Like RELAX_DELAY_SLOT, but indicates that the delay slot requires a
|
|
16-bit instruction.
|
|
|
|
RELAX_DELAY_SLOT_SIZE_FIRST
|
|
Like RELAX_DELAY_SLOT, but indicates that the first implementation of
|
|
the macro is of the wrong size for the branch delay slot.
|
|
|
|
RELAX_DELAY_SLOT_SIZE_SECOND
|
|
Like RELAX_DELAY_SLOT, but indicates that the second implementation of
|
|
the macro is of the wrong size for the branch delay slot.
|
|
|
The frag's "opcode" points to the first fixup for relaxable code.
|
The frag's "opcode" points to the first fixup for relaxable code.
|
|
|
Relaxable macros are generated using a sequence such as:
|
Relaxable macros are generated using a sequence such as:
|
|
|
relax_start (SYMBOL);
|
relax_start (SYMBOL);
|
Line 851... |
Line 1015... |
#define RELAX_SECOND(X) ((X) & 0xff)
|
#define RELAX_SECOND(X) ((X) & 0xff)
|
#define RELAX_USE_SECOND 0x10000
|
#define RELAX_USE_SECOND 0x10000
|
#define RELAX_SECOND_LONGER 0x20000
|
#define RELAX_SECOND_LONGER 0x20000
|
#define RELAX_NOMACRO 0x40000
|
#define RELAX_NOMACRO 0x40000
|
#define RELAX_DELAY_SLOT 0x80000
|
#define RELAX_DELAY_SLOT 0x80000
|
|
#define RELAX_DELAY_SLOT_16BIT 0x100000
|
|
#define RELAX_DELAY_SLOT_SIZE_FIRST 0x200000
|
|
#define RELAX_DELAY_SLOT_SIZE_SECOND 0x400000
|
|
|
/* Branch without likely bit. If label is out of range, we turn:
|
/* Branch without likely bit. If label is out of range, we turn:
|
|
|
beq reg1, reg2, label
|
beq reg1, reg2, label
|
delay slot
|
delay slot
|
Line 974... |
Line 1141... |
#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
|
#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
|
#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
|
#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
|
#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
|
#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
|
#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
|
#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
|
|
|
|
/* For microMIPS code, we use relaxation similar to one we use for
|
|
MIPS16 code. Some instructions that take immediate values support
|
|
two encodings: a small one which takes some small value, and a
|
|
larger one which takes a 16 bit value. As some branches also follow
|
|
this pattern, relaxing these values is required.
|
|
|
|
We can assemble both microMIPS and normal MIPS code in a single
|
|
object. Therefore, we need to support this type of relaxation at
|
|
the same time that we support the relaxation described above. We
|
|
use one of the high bits of the subtype field to distinguish these
|
|
cases.
|
|
|
|
The information we store for this type of relaxation is the argument
|
|
code found in the opcode file for this relocation, the register
|
|
selected as the assembler temporary, whether the branch is
|
|
unconditional, whether it is compact, whether it stores the link
|
|
address implicitly in $ra, whether relaxation of out-of-range 32-bit
|
|
branches to a sequence of instructions is enabled, and whether the
|
|
displacement of a branch is too large to fit as an immediate argument
|
|
of a 16-bit and a 32-bit branch, respectively. */
|
|
#define RELAX_MICROMIPS_ENCODE(type, at, uncond, compact, link, \
|
|
relax32, toofar16, toofar32) \
|
|
(0x40000000 \
|
|
| ((type) & 0xff) \
|
|
| (((at) & 0x1f) << 8) \
|
|
| ((uncond) ? 0x2000 : 0) \
|
|
| ((compact) ? 0x4000 : 0) \
|
|
| ((link) ? 0x8000 : 0) \
|
|
| ((relax32) ? 0x10000 : 0) \
|
|
| ((toofar16) ? 0x20000 : 0) \
|
|
| ((toofar32) ? 0x40000 : 0))
|
|
#define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
|
|
#define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
|
|
#define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
|
|
#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x2000) != 0)
|
|
#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x4000) != 0)
|
|
#define RELAX_MICROMIPS_LINK(i) (((i) & 0x8000) != 0)
|
|
#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x10000) != 0)
|
|
|
|
#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x20000) != 0)
|
|
#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x20000)
|
|
#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x20000)
|
|
#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x40000) != 0)
|
|
#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x40000)
|
|
#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x40000)
|
|
|
/* Is the given value a sign-extended 32-bit value? */
|
/* Is the given value a sign-extended 32-bit value? */
|
#define IS_SEXT_32BIT_NUM(x) \
|
#define IS_SEXT_32BIT_NUM(x) \
|
(((x) &~ (offsetT) 0x7fffffff) == 0 \
|
(((x) &~ (offsetT) 0x7fffffff) == 0 \
|
|| (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
|
|| (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
|
|
|
/* Is the given value a sign-extended 16-bit value? */
|
/* Is the given value a sign-extended 16-bit value? */
|
#define IS_SEXT_16BIT_NUM(x) \
|
#define IS_SEXT_16BIT_NUM(x) \
|
(((x) &~ (offsetT) 0x7fff) == 0 \
|
(((x) &~ (offsetT) 0x7fff) == 0 \
|
|| (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
|
|| (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
|
|
|
|
/* Is the given value a sign-extended 12-bit value? */
|
|
#define IS_SEXT_12BIT_NUM(x) \
|
|
(((((x) & 0xfff) ^ 0x800LL) - 0x800LL) == (x))
|
|
|
/* Is the given value a zero-extended 32-bit value? Or a negated one? */
|
/* Is the given value a zero-extended 32-bit value? Or a negated one? */
|
#define IS_ZEXT_32BIT_NUM(x) \
|
#define IS_ZEXT_32BIT_NUM(x) \
|
(((x) &~ (offsetT) 0xffffffff) == 0 \
|
(((x) &~ (offsetT) 0xffffffff) == 0 \
|
|| (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
|
|| (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
|
|
|
Line 1006... |
Line 1223... |
INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
|
INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
|
|
|
include/opcode/mips.h specifies operand fields using the macros
|
include/opcode/mips.h specifies operand fields using the macros
|
OP_MASK_<FIELD> and OP_SH_<FIELD>. The MIPS16 equivalents start
|
OP_MASK_<FIELD> and OP_SH_<FIELD>. The MIPS16 equivalents start
|
with "MIPS16OP" instead of "OP". */
|
with "MIPS16OP" instead of "OP". */
|
#define INSERT_OPERAND(FIELD, INSN, VALUE) \
|
#define INSERT_OPERAND(MICROMIPS, FIELD, INSN, VALUE) \
|
INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD)
|
do \
|
|
if (!(MICROMIPS)) \
|
|
INSERT_BITS ((INSN).insn_opcode, VALUE, \
|
|
OP_MASK_##FIELD, OP_SH_##FIELD); \
|
|
else \
|
|
INSERT_BITS ((INSN).insn_opcode, VALUE, \
|
|
MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD); \
|
|
while (0)
|
#define MIPS16_INSERT_OPERAND(FIELD, INSN, VALUE) \
|
#define MIPS16_INSERT_OPERAND(FIELD, INSN, VALUE) \
|
INSERT_BITS ((INSN).insn_opcode, VALUE, \
|
INSERT_BITS ((INSN).insn_opcode, VALUE, \
|
MIPS16OP_MASK_##FIELD, MIPS16OP_SH_##FIELD)
|
MIPS16OP_MASK_##FIELD, MIPS16OP_SH_##FIELD)
|
|
|
/* Extract the operand given by FIELD from mips_cl_insn INSN. */
|
/* Extract the operand given by FIELD from mips_cl_insn INSN. */
|
#define EXTRACT_OPERAND(FIELD, INSN) \
|
#define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \
|
EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD)
|
(!(MICROMIPS) \
|
|
? EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD) \
|
|
: EXTRACT_BITS ((INSN).insn_opcode, \
|
|
MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD))
|
#define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \
|
#define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \
|
EXTRACT_BITS ((INSN).insn_opcode, \
|
EXTRACT_BITS ((INSN).insn_opcode, \
|
MIPS16OP_MASK_##FIELD, \
|
MIPS16OP_MASK_##FIELD, \
|
MIPS16OP_SH_##FIELD)
|
MIPS16OP_SH_##FIELD)
|
|
|
|
/* Whether or not we are emitting a branch-likely macro. */
|
|
static bfd_boolean emit_branch_likely_macro = FALSE;
|
|
|
/* Global variables used when generating relaxable macros. See the
|
/* Global variables used when generating relaxable macros. See the
|
comment above RELAX_ENCODE for more details about how relaxation
|
comment above RELAX_ENCODE for more details about how relaxation
|
is used. */
|
is used. */
|
static struct {
|
static struct {
|
/* 0 if we're not emitting a relaxable macro.
|
/* 0 if we're not emitting a relaxable macro.
|
Line 1046... |
Line 1276... |
/* Global variables used to decide whether a macro needs a warning. */
|
/* Global variables used to decide whether a macro needs a warning. */
|
static struct {
|
static struct {
|
/* True if the macro is in a branch delay slot. */
|
/* True if the macro is in a branch delay slot. */
|
bfd_boolean delay_slot_p;
|
bfd_boolean delay_slot_p;
|
|
|
|
/* Set to the length in bytes required if the macro is in a delay slot
|
|
that requires a specific length of instruction, otherwise zero. */
|
|
unsigned int delay_slot_length;
|
|
|
/* For relaxable macros, sizes[0] is the length of the first alternative
|
/* For relaxable macros, sizes[0] is the length of the first alternative
|
in bytes and sizes[1] is the length of the second alternative.
|
in bytes and sizes[1] is the length of the second alternative.
|
For non-relaxable macros, both elements give the length of the
|
For non-relaxable macros, both elements give the length of the
|
macro in bytes. */
|
macro in bytes. */
|
unsigned int sizes[2];
|
unsigned int sizes[2];
|
|
|
|
/* For relaxable macros, first_insn_sizes[0] is the length of the first
|
|
instruction of the first alternative in bytes and first_insn_sizes[1]
|
|
is the length of the first instruction of the second alternative.
|
|
For non-relaxable macros, both elements give the length of the first
|
|
instruction in bytes.
|
|
|
|
Set to zero if we haven't yet seen the first instruction. */
|
|
unsigned int first_insn_sizes[2];
|
|
|
|
/* For relaxable macros, insns[0] is the number of instructions for the
|
|
first alternative and insns[1] is the number of instructions for the
|
|
second alternative.
|
|
|
|
For non-relaxable macros, both elements give the number of
|
|
instructions for the macro. */
|
|
unsigned int insns[2];
|
|
|
/* The first variant frag for this macro. */
|
/* The first variant frag for this macro. */
|
fragS *first_frag;
|
fragS *first_frag;
|
} mips_macro_warning;
|
} mips_macro_warning;
|
|
|
/* Prototypes for static functions. */
|
/* Prototypes for static functions. */
|
Line 1064... |
Line 1315... |
as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
|
as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
|
|
|
enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
|
enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
|
|
|
static void append_insn
|
static void append_insn
|
(struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *);
|
(struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *,
|
|
bfd_boolean expansionp);
|
static void mips_no_prev_insn (void);
|
static void mips_no_prev_insn (void);
|
static void macro_build (expressionS *, const char *, const char *, ...);
|
static void macro_build (expressionS *, const char *, const char *, ...);
|
static void mips16_macro_build
|
static void mips16_macro_build
|
(expressionS *, const char *, const char *, va_list *);
|
(expressionS *, const char *, const char *, va_list *);
|
static void load_register (int, expressionS *, int);
|
static void load_register (int, expressionS *, int);
|
Line 1116... |
Line 1368... |
static void s_mips_file (int);
|
static void s_mips_file (int);
|
static void s_mips_loc (int);
|
static void s_mips_loc (int);
|
static bfd_boolean pic_need_relax (symbolS *, asection *);
|
static bfd_boolean pic_need_relax (symbolS *, asection *);
|
static int relaxed_branch_length (fragS *, asection *, int);
|
static int relaxed_branch_length (fragS *, asection *, int);
|
static int validate_mips_insn (const struct mips_opcode *);
|
static int validate_mips_insn (const struct mips_opcode *);
|
|
static int validate_micromips_insn (const struct mips_opcode *);
|
|
static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
|
|
static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
|
|
|
/* Table and functions used to map between CPU/ISA names, and
|
/* Table and functions used to map between CPU/ISA names, and
|
ISA levels, and CPU numbers. */
|
ISA levels, and CPU numbers. */
|
|
|
struct mips_cpu_info
|
struct mips_cpu_info
|
Line 1135... |
Line 1390... |
#define MIPS_CPU_ASE_DSP 0x0004 /* CPU implements DSP ASE */
|
#define MIPS_CPU_ASE_DSP 0x0004 /* CPU implements DSP ASE */
|
#define MIPS_CPU_ASE_MT 0x0008 /* CPU implements MT ASE */
|
#define MIPS_CPU_ASE_MT 0x0008 /* CPU implements MT ASE */
|
#define MIPS_CPU_ASE_MIPS3D 0x0010 /* CPU implements MIPS-3D ASE */
|
#define MIPS_CPU_ASE_MIPS3D 0x0010 /* CPU implements MIPS-3D ASE */
|
#define MIPS_CPU_ASE_MDMX 0x0020 /* CPU implements MDMX ASE */
|
#define MIPS_CPU_ASE_MDMX 0x0020 /* CPU implements MDMX ASE */
|
#define MIPS_CPU_ASE_DSPR2 0x0040 /* CPU implements DSP R2 ASE */
|
#define MIPS_CPU_ASE_DSPR2 0x0040 /* CPU implements DSP R2 ASE */
|
|
#define MIPS_CPU_ASE_MCU 0x0080 /* CPU implements MCU ASE */
|
|
|
static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
|
static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
|
static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
|
static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
|
static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
|
static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
|
|
|
Line 1268... |
Line 1524... |
|
|
static struct insn_label_list *free_insn_labels;
|
static struct insn_label_list *free_insn_labels;
|
#define label_list tc_segment_info_data.labels
|
#define label_list tc_segment_info_data.labels
|
|
|
static void mips_clear_insn_labels (void);
|
static void mips_clear_insn_labels (void);
|
|
static void mips_mark_labels (void);
|
|
static void mips_compressed_mark_labels (void);
|
|
|
static inline void
|
static inline void
|
mips_clear_insn_labels (void)
|
mips_clear_insn_labels (void)
|
{
|
{
|
register struct insn_label_list **pl;
|
register struct insn_label_list **pl;
|
Line 1286... |
Line 1544... |
*pl = si->label_list;
|
*pl = si->label_list;
|
si->label_list = NULL;
|
si->label_list = NULL;
|
}
|
}
|
}
|
}
|
|
|
|
/* Mark instruction labels in MIPS16/microMIPS mode. */
|
|
|
|
static inline void
|
|
mips_mark_labels (void)
|
|
{
|
|
if (HAVE_CODE_COMPRESSION)
|
|
mips_compressed_mark_labels ();
|
|
}
|
|
|
static char *expr_end;
|
static char *expr_end;
|
|
|
/* Expressions which appear in instructions. These are set by
|
/* Expressions which appear in instructions. These are set by
|
mips_ip. */
|
mips_ip. */
|
Line 1303... |
Line 1569... |
static bfd_reloc_code_real_type imm_reloc[3]
|
static bfd_reloc_code_real_type imm_reloc[3]
|
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
|
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
|
static bfd_reloc_code_real_type offset_reloc[3]
|
static bfd_reloc_code_real_type offset_reloc[3]
|
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
|
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
|
|
|
/* These are set by mips16_ip if an explicit extension is used. */
|
/* This is set to the resulting size of the instruction to be produced
|
|
by mips16_ip if an explicit extension is used or by mips_ip if an
|
|
explicit size is supplied. */
|
|
|
static bfd_boolean mips16_small, mips16_ext;
|
static unsigned int forced_insn_length;
|
|
|
#ifdef OBJ_ELF
|
#ifdef OBJ_ELF
|
/* The pdr segment for per procedure frame/regmask info. Not used for
|
/* The pdr segment for per procedure frame/regmask info. Not used for
|
ECOFF debugging. */
|
ECOFF debugging. */
|
|
|
Line 1357... |
Line 1625... |
abort ();
|
abort ();
|
return NULL;
|
return NULL;
|
}
|
}
|
}
|
}
|
|
|
|
/* Return the length of a microMIPS instruction in bytes. If bits of
|
|
the mask beyond the low 16 are 0, then it is a 16-bit instruction.
|
|
Otherwise assume a 32-bit instruction; 48-bit instructions (0x1f
|
|
major opcode) will require further modifications to the opcode
|
|
table. */
|
|
|
|
static inline unsigned int
|
|
micromips_insn_length (const struct mips_opcode *mo)
|
|
{
|
|
return (mo->mask >> 16) == 0 ? 2 : 4;
|
|
}
|
|
|
/* Return the length of instruction INSN. */
|
/* Return the length of instruction INSN. */
|
|
|
static inline unsigned int
|
static inline unsigned int
|
insn_length (const struct mips_cl_insn *insn)
|
insn_length (const struct mips_cl_insn *insn)
|
{
|
{
|
if (!mips_opts.mips16)
|
if (mips_opts.micromips)
|
return 4;
|
return micromips_insn_length (insn->insn_mo);
|
|
else if (mips_opts.mips16)
|
return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2;
|
return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2;
|
|
else
|
|
return 4;
|
}
|
}
|
|
|
/* Initialise INSN from opcode entry MO. Leave its position unspecified. */
|
/* Initialise INSN from opcode entry MO. Leave its position unspecified. */
|
|
|
static void
|
static void
|
Line 1388... |
Line 1671... |
insn->noreorder_p = (mips_opts.noreorder > 0);
|
insn->noreorder_p = (mips_opts.noreorder > 0);
|
insn->mips16_absolute_jump_p = 0;
|
insn->mips16_absolute_jump_p = 0;
|
insn->complete_p = 0;
|
insn->complete_p = 0;
|
}
|
}
|
|
|
/* Record the current MIPS16 mode in now_seg. */
|
/* Record the current MIPS16/microMIPS mode in now_seg. */
|
|
|
static void
|
static void
|
mips_record_mips16_mode (void)
|
mips_record_compressed_mode (void)
|
{
|
{
|
segment_info_type *si;
|
segment_info_type *si;
|
|
|
si = seg_info (now_seg);
|
si = seg_info (now_seg);
|
if (si->tc_segment_info_data.mips16 != mips_opts.mips16)
|
if (si->tc_segment_info_data.mips16 != mips_opts.mips16)
|
si->tc_segment_info_data.mips16 = mips_opts.mips16;
|
si->tc_segment_info_data.mips16 = mips_opts.mips16;
|
|
if (si->tc_segment_info_data.micromips != mips_opts.micromips)
|
|
si->tc_segment_info_data.micromips = mips_opts.micromips;
|
}
|
}
|
|
|
/* Install INSN at the location specified by its "frag" and "where" fields. */
|
/* Install INSN at the location specified by its "frag" and "where" fields. */
|
|
|
static void
|
static void
|
install_insn (const struct mips_cl_insn *insn)
|
install_insn (const struct mips_cl_insn *insn)
|
{
|
{
|
char *f = insn->frag->fr_literal + insn->where;
|
char *f = insn->frag->fr_literal + insn->where;
|
if (!mips_opts.mips16)
|
if (!HAVE_CODE_COMPRESSION)
|
md_number_to_chars (f, insn->insn_opcode, 4);
|
md_number_to_chars (f, insn->insn_opcode, 4);
|
|
else if (mips_opts.micromips)
|
|
{
|
|
unsigned int length = insn_length (insn);
|
|
if (length == 2)
|
|
md_number_to_chars (f, insn->insn_opcode, 2);
|
|
else if (length == 4)
|
|
{
|
|
md_number_to_chars (f, insn->insn_opcode >> 16, 2);
|
|
f += 2;
|
|
md_number_to_chars (f, insn->insn_opcode & 0xffff, 2);
|
|
}
|
|
else
|
|
as_bad (_("48-bit microMIPS instructions are not supported"));
|
|
}
|
else if (insn->mips16_absolute_jump_p)
|
else if (insn->mips16_absolute_jump_p)
|
{
|
{
|
md_number_to_chars (f, insn->insn_opcode >> 16, 2);
|
md_number_to_chars (f, insn->insn_opcode >> 16, 2);
|
md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2);
|
md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2);
|
}
|
}
|
Line 1422... |
Line 1721... |
md_number_to_chars (f, 0xf000 | insn->extend, 2);
|
md_number_to_chars (f, 0xf000 | insn->extend, 2);
|
f += 2;
|
f += 2;
|
}
|
}
|
md_number_to_chars (f, insn->insn_opcode, 2);
|
md_number_to_chars (f, insn->insn_opcode, 2);
|
}
|
}
|
mips_record_mips16_mode ();
|
mips_record_compressed_mode ();
|
}
|
}
|
|
|
/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
|
/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
|
and install the opcode in the new location. */
|
and install the opcode in the new location. */
|
|
|
Line 1486... |
Line 1785... |
else
|
else
|
history[i] = *insn;
|
history[i] = *insn;
|
}
|
}
|
}
|
}
|
|
|
/* Emit a nop instruction, recording it in the history buffer. */
|
|
|
|
static void
|
|
emit_nop (void)
|
|
{
|
|
add_fixed_insn (NOP_INSN);
|
|
insert_into_history (0, 1, NOP_INSN);
|
|
}
|
|
|
|
/* Initialize vr4120_conflicts. There is a bit of duplication here:
|
/* Initialize vr4120_conflicts. There is a bit of duplication here:
|
the idea is to make it obvious at a glance that each errata is
|
the idea is to make it obvious at a glance that each errata is
|
included. */
|
included. */
|
|
|
static void
|
static void
|
Line 1775... |
Line 2065... |
static const struct regname reg_names_n32n64[] = {
|
static const struct regname reg_names_n32n64[] = {
|
N32N64_SYMBOLIC_REGISTER_NAMES,
|
N32N64_SYMBOLIC_REGISTER_NAMES,
|
{0, 0}
|
{0, 0}
|
};
|
};
|
|
|
|
/* Check if S points at a valid register specifier according to TYPES.
|
|
If so, then return 1, advance S to consume the specifier and store
|
|
the register's number in REGNOP, otherwise return 0. */
|
|
|
static int
|
static int
|
reg_lookup (char **s, unsigned int types, unsigned int *regnop)
|
reg_lookup (char **s, unsigned int types, unsigned int *regnop)
|
{
|
{
|
symbolS *symbolP;
|
symbolS *symbolP;
|
char *e;
|
char *e;
|
Line 1828... |
Line 2122... |
if (regnop)
|
if (regnop)
|
*regnop = reg;
|
*regnop = reg;
|
return reg >= 0;
|
return reg >= 0;
|
}
|
}
|
|
|
|
/* Check if S points at a valid register list according to TYPES.
|
|
If so, then return 1, advance S to consume the list and store
|
|
the registers present on the list as a bitmask of ones in REGLISTP,
|
|
otherwise return 0. A valid list comprises a comma-separated
|
|
enumeration of valid single registers and/or dash-separated
|
|
contiguous register ranges as determined by their numbers.
|
|
|
|
As a special exception if one of s0-s7 registers is specified as
|
|
the range's lower delimiter and s8 (fp) is its upper one, then no
|
|
registers whose numbers place them between s7 and s8 (i.e. $24-$29)
|
|
are selected; they have to be listed separately if needed. */
|
|
|
|
static int
|
|
reglist_lookup (char **s, unsigned int types, unsigned int *reglistp)
|
|
{
|
|
unsigned int reglist = 0;
|
|
unsigned int lastregno;
|
|
bfd_boolean ok = TRUE;
|
|
unsigned int regmask;
|
|
char *s_endlist = *s;
|
|
char *s_reset = *s;
|
|
unsigned int regno;
|
|
|
|
while (reg_lookup (s, types, ®no))
|
|
{
|
|
lastregno = regno;
|
|
if (**s == '-')
|
|
{
|
|
(*s)++;
|
|
ok = reg_lookup (s, types, &lastregno);
|
|
if (ok && lastregno < regno)
|
|
ok = FALSE;
|
|
if (!ok)
|
|
break;
|
|
}
|
|
|
|
if (lastregno == FP && regno >= S0 && regno <= S7)
|
|
{
|
|
lastregno = S7;
|
|
reglist |= 1 << FP;
|
|
}
|
|
regmask = 1 << lastregno;
|
|
regmask = (regmask << 1) - 1;
|
|
regmask ^= (1 << regno) - 1;
|
|
reglist |= regmask;
|
|
|
|
s_endlist = *s;
|
|
if (**s != ',')
|
|
break;
|
|
(*s)++;
|
|
}
|
|
|
|
if (ok)
|
|
*s = s_endlist;
|
|
else
|
|
*s = s_reset;
|
|
if (reglistp)
|
|
*reglistp = reglist;
|
|
return ok && reglist != 0;
|
|
}
|
|
|
/* Return TRUE if opcode MO is valid on the currently selected ISA and
|
/* Return TRUE if opcode MO is valid on the currently selected ISA and
|
architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
|
architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
|
|
|
static bfd_boolean
|
static bfd_boolean
|
is_opcode_valid (const struct mips_opcode *mo)
|
is_opcode_valid (const struct mips_opcode *mo)
|
Line 1851... |
Line 2206... |
isa |= INSN_MT;
|
isa |= INSN_MT;
|
if (mips_opts.ase_mips3d)
|
if (mips_opts.ase_mips3d)
|
isa |= INSN_MIPS3D;
|
isa |= INSN_MIPS3D;
|
if (mips_opts.ase_smartmips)
|
if (mips_opts.ase_smartmips)
|
isa |= INSN_SMARTMIPS;
|
isa |= INSN_SMARTMIPS;
|
|
if (mips_opts.ase_mcu)
|
|
isa |= INSN_MCU;
|
|
|
/* Don't accept instructions based on the ISA if the CPU does not implement
|
/* Don't accept instructions based on the ISA if the CPU does not implement
|
all the coprocessor insns. */
|
all the coprocessor insns. */
|
if (NO_ISA_COP (mips_opts.arch)
|
if (NO_ISA_COP (mips_opts.arch)
|
&& COP_INSN (mo->pinfo))
|
&& COP_INSN (mo->pinfo))
|
Line 1893... |
Line 2250... |
is_opcode_valid_16 (const struct mips_opcode *mo)
|
is_opcode_valid_16 (const struct mips_opcode *mo)
|
{
|
{
|
return OPCODE_IS_MEMBER (mo, mips_opts.isa, mips_opts.arch) ? TRUE : FALSE;
|
return OPCODE_IS_MEMBER (mo, mips_opts.isa, mips_opts.arch) ? TRUE : FALSE;
|
}
|
}
|
|
|
|
/* Return TRUE if the size of the microMIPS opcode MO matches one
|
|
explicitly requested. Always TRUE in the standard MIPS mode. */
|
|
|
|
static bfd_boolean
|
|
is_size_valid (const struct mips_opcode *mo)
|
|
{
|
|
if (!mips_opts.micromips)
|
|
return TRUE;
|
|
|
|
if (!forced_insn_length)
|
|
return TRUE;
|
|
if (mo->pinfo == INSN_MACRO)
|
|
return FALSE;
|
|
return forced_insn_length == micromips_insn_length (mo);
|
|
}
|
|
|
|
/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
|
|
of the preceding instruction. Always TRUE in the standard MIPS mode. */
|
|
|
|
static bfd_boolean
|
|
is_delay_slot_valid (const struct mips_opcode *mo)
|
|
{
|
|
if (!mips_opts.micromips)
|
|
return TRUE;
|
|
|
|
if (mo->pinfo == INSN_MACRO)
|
|
return TRUE;
|
|
if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
|
|
&& micromips_insn_length (mo) != 4)
|
|
return FALSE;
|
|
if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
|
|
&& micromips_insn_length (mo) != 2)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
/* This function is called once, at assembler startup time. It should set up
|
/* 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. */
|
all the tables, etc. that the MD part of the assembler will need. */
|
|
|
void
|
void
|
md_begin (void)
|
md_begin (void)
|
Line 1978... |
Line 2372... |
}
|
}
|
while (i < bfd_mips16_num_opcodes
|
while (i < bfd_mips16_num_opcodes
|
&& strcmp (mips16_opcodes[i].name, name) == 0);
|
&& strcmp (mips16_opcodes[i].name, name) == 0);
|
}
|
}
|
|
|
|
micromips_op_hash = hash_new ();
|
|
|
|
i = 0;
|
|
while (i < bfd_micromips_num_opcodes)
|
|
{
|
|
const char *name = micromips_opcodes[i].name;
|
|
|
|
retval = hash_insert (micromips_op_hash, name,
|
|
(void *) µmips_opcodes[i]);
|
|
if (retval != NULL)
|
|
as_fatal (_("internal: can't hash `%s': %s"),
|
|
micromips_opcodes[i].name, retval);
|
|
do
|
|
if (micromips_opcodes[i].pinfo != INSN_MACRO)
|
|
{
|
|
struct mips_cl_insn *micromips_nop_insn;
|
|
|
|
if (!validate_micromips_insn (µmips_opcodes[i]))
|
|
broken = 1;
|
|
|
|
if (micromips_insn_length (micromips_opcodes + i) == 2)
|
|
micromips_nop_insn = µmips_nop16_insn;
|
|
else if (micromips_insn_length (micromips_opcodes + i) == 4)
|
|
micromips_nop_insn = µmips_nop32_insn;
|
|
else
|
|
continue;
|
|
|
|
if (micromips_nop_insn->insn_mo == NULL
|
|
&& strcmp (name, "nop") == 0)
|
|
{
|
|
create_insn (micromips_nop_insn, micromips_opcodes + i);
|
|
micromips_nop_insn->fixed_p = 1;
|
|
}
|
|
}
|
|
while (++i < bfd_micromips_num_opcodes
|
|
&& strcmp (micromips_opcodes[i].name, name) == 0);
|
|
}
|
|
|
if (broken)
|
if (broken)
|
as_fatal (_("Broken assembler. No assembly attempted."));
|
as_fatal (_("Broken assembler. No assembly attempted."));
|
|
|
/* We add all the general register names to the symbol table. This
|
/* We add all the general register names to the symbol table. This
|
helps us detect invalid uses of them. */
|
helps us detect invalid uses of them. */
|
Line 2109... |
Line 2541... |
}
|
}
|
|
|
void
|
void
|
md_mips_end (void)
|
md_mips_end (void)
|
{
|
{
|
|
mips_emit_delays ();
|
if (! ECOFF_DEBUGGING)
|
if (! ECOFF_DEBUGGING)
|
md_obj_end ();
|
md_obj_end ();
|
}
|
}
|
|
|
void
|
void
|
Line 2157... |
Line 2590... |
macro_end ();
|
macro_end ();
|
}
|
}
|
else
|
else
|
{
|
{
|
if (imm_expr.X_op != O_absent)
|
if (imm_expr.X_op != O_absent)
|
append_insn (&insn, &imm_expr, imm_reloc);
|
append_insn (&insn, &imm_expr, imm_reloc, FALSE);
|
else if (offset_expr.X_op != O_absent)
|
else if (offset_expr.X_op != O_absent)
|
append_insn (&insn, &offset_expr, offset_reloc);
|
append_insn (&insn, &offset_expr, offset_reloc, FALSE);
|
else
|
else
|
append_insn (&insn, NULL, unused_reloc);
|
append_insn (&insn, NULL, unused_reloc, FALSE);
|
}
|
}
|
}
|
}
|
|
|
/* Convenience functions for abstracting away the differences between
|
/* Convenience functions for abstracting away the differences between
|
MIPS16 and non-MIPS16 relocations. */
|
MIPS16 and non-MIPS16 relocations. */
|
Line 2188... |
Line 2621... |
return FALSE;
|
return FALSE;
|
}
|
}
|
}
|
}
|
|
|
static inline bfd_boolean
|
static inline bfd_boolean
|
|
micromips_reloc_p (bfd_reloc_code_real_type reloc)
|
|
{
|
|
switch (reloc)
|
|
{
|
|
case BFD_RELOC_MICROMIPS_7_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_10_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_16_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_GPREL16:
|
|
case BFD_RELOC_MICROMIPS_JMP:
|
|
case BFD_RELOC_MICROMIPS_HI16:
|
|
case BFD_RELOC_MICROMIPS_HI16_S:
|
|
case BFD_RELOC_MICROMIPS_LO16:
|
|
case BFD_RELOC_MICROMIPS_LITERAL:
|
|
case BFD_RELOC_MICROMIPS_GOT16:
|
|
case BFD_RELOC_MICROMIPS_CALL16:
|
|
case BFD_RELOC_MICROMIPS_GOT_HI16:
|
|
case BFD_RELOC_MICROMIPS_GOT_LO16:
|
|
case BFD_RELOC_MICROMIPS_CALL_HI16:
|
|
case BFD_RELOC_MICROMIPS_CALL_LO16:
|
|
case BFD_RELOC_MICROMIPS_SUB:
|
|
case BFD_RELOC_MICROMIPS_GOT_PAGE:
|
|
case BFD_RELOC_MICROMIPS_GOT_OFST:
|
|
case BFD_RELOC_MICROMIPS_GOT_DISP:
|
|
case BFD_RELOC_MICROMIPS_HIGHEST:
|
|
case BFD_RELOC_MICROMIPS_HIGHER:
|
|
case BFD_RELOC_MICROMIPS_SCN_DISP:
|
|
case BFD_RELOC_MICROMIPS_JALR:
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static inline bfd_boolean
|
|
jmp_reloc_p (bfd_reloc_code_real_type reloc)
|
|
{
|
|
return reloc == BFD_RELOC_MIPS_JMP || reloc == BFD_RELOC_MICROMIPS_JMP;
|
|
}
|
|
|
|
static inline bfd_boolean
|
got16_reloc_p (bfd_reloc_code_real_type reloc)
|
got16_reloc_p (bfd_reloc_code_real_type reloc)
|
{
|
{
|
return reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16;
|
return (reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16
|
|
|| reloc == BFD_RELOC_MICROMIPS_GOT16);
|
}
|
}
|
|
|
static inline bfd_boolean
|
static inline bfd_boolean
|
hi16_reloc_p (bfd_reloc_code_real_type reloc)
|
hi16_reloc_p (bfd_reloc_code_real_type reloc)
|
{
|
{
|
return reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S;
|
return (reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S
|
|
|| reloc == BFD_RELOC_MICROMIPS_HI16_S);
|
}
|
}
|
|
|
static inline bfd_boolean
|
static inline bfd_boolean
|
lo16_reloc_p (bfd_reloc_code_real_type reloc)
|
lo16_reloc_p (bfd_reloc_code_real_type reloc)
|
{
|
{
|
return reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16;
|
return (reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16
|
|
|| reloc == BFD_RELOC_MICROMIPS_LO16);
|
|
}
|
|
|
|
static inline bfd_boolean
|
|
jalr_reloc_p (bfd_reloc_code_real_type reloc)
|
|
{
|
|
return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
|
}
|
}
|
|
|
/* Return true if the given relocation might need a matching %lo().
|
/* Return true if the given relocation might need a matching %lo().
|
This is only "might" because SVR4 R_MIPS_GOT16 relocations only
|
This is only "might" because SVR4 R_MIPS_GOT16 relocations only
|
need a matching %lo() when applied to local symbols. */
|
need a matching %lo() when applied to local symbols. */
|
Line 2225... |
Line 2708... |
reloc_needs_lo_p. */
|
reloc_needs_lo_p. */
|
|
|
static inline bfd_reloc_code_real_type
|
static inline bfd_reloc_code_real_type
|
matching_lo_reloc (bfd_reloc_code_real_type reloc)
|
matching_lo_reloc (bfd_reloc_code_real_type reloc)
|
{
|
{
|
return mips16_reloc_p (reloc) ? BFD_RELOC_MIPS16_LO16 : BFD_RELOC_LO16;
|
return (mips16_reloc_p (reloc) ? BFD_RELOC_MIPS16_LO16
|
|
: (micromips_reloc_p (reloc) ? BFD_RELOC_MICROMIPS_LO16
|
|
: BFD_RELOC_LO16));
|
}
|
}
|
|
|
/* Return true if the given fixup is followed by a matching R_MIPS_LO16
|
/* Return true if the given fixup is followed by a matching R_MIPS_LO16
|
relocation. */
|
relocation. */
|
|
|
Line 2240... |
Line 2725... |
&& fixp->fx_next->fx_r_type == matching_lo_reloc (fixp->fx_r_type)
|
&& fixp->fx_next->fx_r_type == matching_lo_reloc (fixp->fx_r_type)
|
&& fixp->fx_addsy == fixp->fx_next->fx_addsy
|
&& fixp->fx_addsy == fixp->fx_next->fx_addsy
|
&& fixp->fx_offset == fixp->fx_next->fx_offset);
|
&& fixp->fx_offset == fixp->fx_next->fx_offset);
|
}
|
}
|
|
|
/* See whether instruction IP reads register REG. CLASS is the type
|
|
of register. */
|
|
|
|
static int
|
|
insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg,
|
|
enum mips_regclass regclass)
|
|
{
|
|
if (regclass == MIPS16_REG)
|
|
{
|
|
gas_assert (mips_opts.mips16);
|
|
reg = mips16_to_32_reg_map[reg];
|
|
regclass = MIPS_GR_REG;
|
|
}
|
|
|
|
/* Don't report on general register ZERO, since it never changes. */
|
|
if (regclass == MIPS_GR_REG && reg == ZERO)
|
|
return 0;
|
|
|
|
if (regclass == MIPS_FP_REG)
|
|
{
|
|
gas_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)
|
|
&& ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1)
|
|
== (reg &~ (unsigned) 1)))
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
|
|
&& ((EXTRACT_OPERAND (FT, *ip) & ~(unsigned) 1)
|
|
== (reg &~ (unsigned) 1)))
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo2 & INSN2_READ_FPR_Z)
|
|
&& ((EXTRACT_OPERAND (FZ, *ip) & ~(unsigned) 1)
|
|
== (reg &~ (unsigned) 1)))
|
|
return 1;
|
|
}
|
|
else if (! mips_opts.mips16)
|
|
{
|
|
if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
|
|
&& EXTRACT_OPERAND (RS, *ip) == reg)
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
|
|
&& EXTRACT_OPERAND (RT, *ip) == reg)
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_D)
|
|
&& EXTRACT_OPERAND (RD, *ip) == reg)
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_Z)
|
|
&& EXTRACT_OPERAND (RZ, *ip) == reg)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
|
|
&& mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)] == reg)
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
|
|
&& mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)] == reg)
|
|
return 1;
|
|
if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
|
|
&& (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]
|
|
== 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)
|
|
&& MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function returns true if modifying a register requires a
|
/* This function returns true if modifying a register requires a
|
delay. */
|
delay. */
|
|
|
static int
|
static int
|
reg_needs_delay (unsigned int reg)
|
reg_needs_delay (unsigned int reg)
|
Line 2341... |
Line 2744... |
{
|
{
|
/* A load from a coprocessor or from memory. All load delays
|
/* A load from a coprocessor or from memory. All load delays
|
delay the use of general register rt for one instruction. */
|
delay the use of general register rt for one instruction. */
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
know (prev_pinfo & INSN_WRITE_GPR_T);
|
know (prev_pinfo & INSN_WRITE_GPR_T);
|
if (reg == EXTRACT_OPERAND (RT, history[0]))
|
if (reg == EXTRACT_OPERAND (mips_opts.micromips, RT, history[0]))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
Line 2362... |
Line 2765... |
for (l = si->label_list; l != NULL; l = l->next)
|
for (l = si->label_list; l != NULL; l = l->next)
|
{
|
{
|
gas_assert (S_GET_SEGMENT (l->label) == now_seg);
|
gas_assert (S_GET_SEGMENT (l->label) == now_seg);
|
symbol_set_frag (l->label, frag_now);
|
symbol_set_frag (l->label, frag_now);
|
val = (valueT) frag_now_fix ();
|
val = (valueT) frag_now_fix ();
|
/* mips16 text labels are stored as odd. */
|
/* MIPS16/microMIPS text labels are stored as odd. */
|
if (mips_opts.mips16)
|
if (HAVE_CODE_COMPRESSION)
|
++val;
|
++val;
|
S_SET_VALUE (l->label, val);
|
S_SET_VALUE (l->label, val);
|
}
|
}
|
}
|
}
|
|
|
Line 2391... |
Line 2794... |
#endif
|
#endif
|
}
|
}
|
return linkonce;
|
return linkonce;
|
}
|
}
|
|
|
/* Mark instruction labels in mips16 mode. This permits the linker to
|
/* Mark instruction labels in MIPS16/microMIPS mode. This permits the
|
handle them specially, such as generating jalx instructions when
|
linker to handle them specially, such as generating jalx instructions
|
needed. We also make them odd for the duration of the assembly, in
|
when needed. We also make them odd for the duration of the assembly,
|
order to generate the right sort of code. We will make them even
|
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
|
in the adjust_symtab routine, while leaving them marked. This is
|
convenient for the debugger and the disassembler. The linker knows
|
convenient for the debugger and the disassembler. The linker knows
|
to make them odd again. */
|
to make them odd again. */
|
|
|
static void
|
static void
|
mips16_mark_labels (void)
|
mips_compressed_mark_labels (void)
|
{
|
{
|
segment_info_type *si = seg_info (now_seg);
|
segment_info_type *si = seg_info (now_seg);
|
struct insn_label_list *l;
|
struct insn_label_list *l;
|
|
|
if (!mips_opts.mips16)
|
gas_assert (HAVE_CODE_COMPRESSION);
|
return;
|
|
|
|
for (l = si->label_list; l != NULL; l = l->next)
|
for (l = si->label_list; l != NULL; l = l->next)
|
{
|
{
|
symbolS *label = l->label;
|
symbolS *label = l->label;
|
|
|
#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
|
#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
|
if (IS_ELF)
|
if (IS_ELF)
|
|
{
|
|
if (mips_opts.mips16)
|
S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
|
S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
|
|
else
|
|
S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
|
|
}
|
#endif
|
#endif
|
if ((S_GET_VALUE (label) & 1) == 0
|
if ((S_GET_VALUE (label) & 1) == 0
|
/* Don't adjust the address if the label is global or weak, or
|
/* Don't adjust the address if the label is global or weak, or
|
in a link-once section, since we'll be emitting symbol reloc
|
in a link-once section, since we'll be emitting symbol reloc
|
references to it which will be patched up by the linker, and
|
references to it which will be patched up by the linker, and
|
the final value of the symbol may or may not be MIPS16. */
|
the final value of the symbol may or may not be MIPS16/microMIPS. */
|
&& ! S_IS_WEAK (label)
|
&& ! S_IS_WEAK (label)
|
&& ! S_IS_EXTERNAL (label)
|
&& ! S_IS_EXTERNAL (label)
|
&& ! s_is_linkonce (label, now_seg))
|
&& ! s_is_linkonce (label, now_seg))
|
S_SET_VALUE (label, S_GET_VALUE (label) | 1);
|
S_SET_VALUE (label, S_GET_VALUE (label) | 1);
|
}
|
}
|
Line 2474... |
Line 2881... |
gas_assert (mips_relax.sequence == 2);
|
gas_assert (mips_relax.sequence == 2);
|
relax_close_frag ();
|
relax_close_frag ();
|
mips_relax.sequence = 0;
|
mips_relax.sequence = 0;
|
}
|
}
|
|
|
/* Classify an instruction according to the FIX_VR4120_* enumeration.
|
/* Return true if IP is a delayed branch or jump. */
|
Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
|
|
by VR4120 errata. */
|
|
|
|
static unsigned int
|
static inline bfd_boolean
|
classify_vr4120_insn (const char *name)
|
delayed_branch_p (const struct mips_cl_insn *ip)
|
{
|
{
|
if (strncmp (name, "macc", 4) == 0)
|
return (ip->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
|
return FIX_VR4120_MACC;
|
| INSN_COND_BRANCH_DELAY
|
if (strncmp (name, "dmacc", 5) == 0)
|
| INSN_COND_BRANCH_LIKELY)) != 0;
|
return FIX_VR4120_DMACC;
|
|
if (strncmp (name, "mult", 4) == 0)
|
|
return FIX_VR4120_MULT;
|
|
if (strncmp (name, "dmult", 5) == 0)
|
|
return FIX_VR4120_DMULT;
|
|
if (strstr (name, "div"))
|
|
return FIX_VR4120_DIV;
|
|
if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
|
|
return FIX_VR4120_MTHILO;
|
|
return NUM_FIX_VR4120_CLASSES;
|
|
}
|
}
|
|
|
#define INSN_ERET 0x42000018
|
/* Return true if IP is a compact branch or jump. */
|
#define INSN_DERET 0x4200001f
|
|
|
static inline bfd_boolean
|
/* Return the number of instructions that must separate INSN1 and INSN2,
|
compact_branch_p (const struct mips_cl_insn *ip)
|
where INSN1 is the earlier instruction. Return the worst-case value
|
{
|
for any INSN2 if INSN2 is null. */
|
if (mips_opts.mips16)
|
|
return (ip->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
|
|
| MIPS16_INSN_COND_BRANCH)) != 0;
|
|
else
|
|
return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
|
|
| INSN2_COND_BRANCH)) != 0;
|
|
}
|
|
|
|
/* Return true if IP is an unconditional branch or jump. */
|
|
|
|
static inline bfd_boolean
|
|
uncond_branch_p (const struct mips_cl_insn *ip)
|
|
{
|
|
return ((ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0
|
|
|| (mips_opts.mips16
|
|
? (ip->insn_mo->pinfo & MIPS16_INSN_UNCOND_BRANCH) != 0
|
|
: (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0));
|
|
}
|
|
|
|
/* Return true if IP is a branch-likely instruction. */
|
|
|
|
static inline bfd_boolean
|
|
branch_likely_p (const struct mips_cl_insn *ip)
|
|
{
|
|
return (ip->insn_mo->pinfo & INSN_COND_BRANCH_LIKELY) != 0;
|
|
}
|
|
|
|
/* Return the type of nop that should be used to fill the delay slot
|
|
of delayed branch IP. */
|
|
|
|
static struct mips_cl_insn *
|
|
get_delay_slot_nop (const struct mips_cl_insn *ip)
|
|
{
|
|
if (mips_opts.micromips
|
|
&& (ip->insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
|
|
return µmips_nop32_insn;
|
|
return NOP_INSN;
|
|
}
|
|
|
|
/* Return the mask of core registers that IP reads or writes. */
|
|
|
|
static unsigned int
|
|
gpr_mod_mask (const struct mips_cl_insn *ip)
|
|
{
|
|
unsigned long pinfo2;
|
|
unsigned int mask;
|
|
|
|
mask = 0;
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (mips_opts.micromips)
|
|
{
|
|
if (pinfo2 & INSN2_MOD_GPR_MD)
|
|
mask |= 1 << micromips_to_32_reg_d_map[EXTRACT_OPERAND (1, MD, *ip)];
|
|
if (pinfo2 & INSN2_MOD_GPR_MF)
|
|
mask |= 1 << micromips_to_32_reg_f_map[EXTRACT_OPERAND (1, MF, *ip)];
|
|
if (pinfo2 & INSN2_MOD_SP)
|
|
mask |= 1 << SP;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/* Return the mask of core registers that IP reads. */
|
|
|
|
static unsigned int
|
|
gpr_read_mask (const struct mips_cl_insn *ip)
|
|
{
|
|
unsigned long pinfo, pinfo2;
|
|
unsigned int mask;
|
|
|
|
mask = gpr_mod_mask (ip);
|
|
pinfo = ip->insn_mo->pinfo;
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (mips_opts.mips16)
|
|
{
|
|
if (pinfo & MIPS16_INSN_READ_X)
|
|
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
|
|
if (pinfo & MIPS16_INSN_READ_Y)
|
|
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
|
|
if (pinfo & MIPS16_INSN_READ_T)
|
|
mask |= 1 << TREG;
|
|
if (pinfo & MIPS16_INSN_READ_SP)
|
|
mask |= 1 << SP;
|
|
if (pinfo & MIPS16_INSN_READ_31)
|
|
mask |= 1 << RA;
|
|
if (pinfo & MIPS16_INSN_READ_Z)
|
|
mask |= 1 << (mips16_to_32_reg_map
|
|
[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
|
|
if (pinfo & MIPS16_INSN_READ_GPR_X)
|
|
mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
|
|
}
|
|
else
|
|
{
|
|
if (pinfo2 & INSN2_READ_GPR_D)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
|
|
if (pinfo & INSN_READ_GPR_T)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
|
|
if (pinfo & INSN_READ_GPR_S)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
|
|
if (pinfo2 & INSN2_READ_GP)
|
|
mask |= 1 << GP;
|
|
if (pinfo2 & INSN2_READ_GPR_31)
|
|
mask |= 1 << RA;
|
|
if (pinfo2 & INSN2_READ_GPR_Z)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
|
|
}
|
|
if (mips_opts.micromips)
|
|
{
|
|
if (pinfo2 & INSN2_READ_GPR_MC)
|
|
mask |= 1 << micromips_to_32_reg_c_map[EXTRACT_OPERAND (1, MC, *ip)];
|
|
if (pinfo2 & INSN2_READ_GPR_ME)
|
|
mask |= 1 << micromips_to_32_reg_e_map[EXTRACT_OPERAND (1, ME, *ip)];
|
|
if (pinfo2 & INSN2_READ_GPR_MG)
|
|
mask |= 1 << micromips_to_32_reg_g_map[EXTRACT_OPERAND (1, MG, *ip)];
|
|
if (pinfo2 & INSN2_READ_GPR_MJ)
|
|
mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
|
|
if (pinfo2 & INSN2_READ_GPR_MMN)
|
|
{
|
|
mask |= 1 << micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
|
|
mask |= 1 << micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
|
|
}
|
|
if (pinfo2 & INSN2_READ_GPR_MP)
|
|
mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
|
|
if (pinfo2 & INSN2_READ_GPR_MQ)
|
|
mask |= 1 << micromips_to_32_reg_q_map[EXTRACT_OPERAND (1, MQ, *ip)];
|
|
}
|
|
/* Don't include register 0. */
|
|
return mask & ~1;
|
|
}
|
|
|
|
/* Return the mask of core registers that IP writes. */
|
|
|
|
static unsigned int
|
|
gpr_write_mask (const struct mips_cl_insn *ip)
|
|
{
|
|
unsigned long pinfo, pinfo2;
|
|
unsigned int mask;
|
|
|
|
mask = gpr_mod_mask (ip);
|
|
pinfo = ip->insn_mo->pinfo;
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (mips_opts.mips16)
|
|
{
|
|
if (pinfo & MIPS16_INSN_WRITE_X)
|
|
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
|
|
if (pinfo & MIPS16_INSN_WRITE_Y)
|
|
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
|
|
if (pinfo & MIPS16_INSN_WRITE_Z)
|
|
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
|
|
if (pinfo & MIPS16_INSN_WRITE_T)
|
|
mask |= 1 << TREG;
|
|
if (pinfo & MIPS16_INSN_WRITE_SP)
|
|
mask |= 1 << SP;
|
|
if (pinfo & MIPS16_INSN_WRITE_31)
|
|
mask |= 1 << RA;
|
|
if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
|
|
mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
|
|
}
|
|
else
|
|
{
|
|
if (pinfo & INSN_WRITE_GPR_D)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
|
|
if (pinfo & INSN_WRITE_GPR_T)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
|
|
if (pinfo & INSN_WRITE_GPR_S)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
|
|
if (pinfo & INSN_WRITE_GPR_31)
|
|
mask |= 1 << RA;
|
|
if (pinfo2 & INSN2_WRITE_GPR_Z)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
|
|
}
|
|
if (mips_opts.micromips)
|
|
{
|
|
if (pinfo2 & INSN2_WRITE_GPR_MB)
|
|
mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
|
|
if (pinfo2 & INSN2_WRITE_GPR_MHI)
|
|
{
|
|
mask |= 1 << micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
|
|
mask |= 1 << micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *ip)];
|
|
}
|
|
if (pinfo2 & INSN2_WRITE_GPR_MJ)
|
|
mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
|
|
if (pinfo2 & INSN2_WRITE_GPR_MP)
|
|
mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
|
|
}
|
|
/* Don't include register 0. */
|
|
return mask & ~1;
|
|
}
|
|
|
|
/* Return the mask of floating-point registers that IP reads. */
|
|
|
|
static unsigned int
|
|
fpr_read_mask (const struct mips_cl_insn *ip)
|
|
{
|
|
unsigned long pinfo, pinfo2;
|
|
unsigned int mask;
|
|
|
|
mask = 0;
|
|
pinfo = ip->insn_mo->pinfo;
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (!mips_opts.mips16)
|
|
{
|
|
if (pinfo2 & INSN2_READ_FPR_D)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
|
|
if (pinfo & INSN_READ_FPR_S)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
|
|
if (pinfo & INSN_READ_FPR_T)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
|
|
if (pinfo & INSN_READ_FPR_R)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FR, *ip);
|
|
if (pinfo2 & INSN2_READ_FPR_Z)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
|
|
}
|
|
/* Conservatively treat all operands to an FP_D instruction are doubles.
|
|
(This is overly pessimistic for things like cvt.d.s.) */
|
|
if (HAVE_32BIT_FPRS && (pinfo & FP_D))
|
|
mask |= mask << 1;
|
|
return mask;
|
|
}
|
|
|
|
/* Return the mask of floating-point registers that IP writes. */
|
|
|
|
static unsigned int
|
|
fpr_write_mask (const struct mips_cl_insn *ip)
|
|
{
|
|
unsigned long pinfo, pinfo2;
|
|
unsigned int mask;
|
|
|
|
mask = 0;
|
|
pinfo = ip->insn_mo->pinfo;
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (!mips_opts.mips16)
|
|
{
|
|
if (pinfo & INSN_WRITE_FPR_D)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
|
|
if (pinfo & INSN_WRITE_FPR_S)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
|
|
if (pinfo & INSN_WRITE_FPR_T)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
|
|
if (pinfo2 & INSN2_WRITE_FPR_Z)
|
|
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
|
|
}
|
|
/* Conservatively treat all operands to an FP_D instruction are doubles.
|
|
(This is overly pessimistic for things like cvt.s.d.) */
|
|
if (HAVE_32BIT_FPRS && (pinfo & FP_D))
|
|
mask |= mask << 1;
|
|
return mask;
|
|
}
|
|
|
|
/* Classify an instruction according to the FIX_VR4120_* enumeration.
|
|
Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
|
|
by VR4120 errata. */
|
|
|
|
static unsigned int
|
|
classify_vr4120_insn (const char *name)
|
|
{
|
|
if (strncmp (name, "macc", 4) == 0)
|
|
return FIX_VR4120_MACC;
|
|
if (strncmp (name, "dmacc", 5) == 0)
|
|
return FIX_VR4120_DMACC;
|
|
if (strncmp (name, "mult", 4) == 0)
|
|
return FIX_VR4120_MULT;
|
|
if (strncmp (name, "dmult", 5) == 0)
|
|
return FIX_VR4120_DMULT;
|
|
if (strstr (name, "div"))
|
|
return FIX_VR4120_DIV;
|
|
if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
|
|
return FIX_VR4120_MTHILO;
|
|
return NUM_FIX_VR4120_CLASSES;
|
|
}
|
|
|
|
#define INSN_ERET 0x42000018
|
|
#define INSN_DERET 0x4200001f
|
|
|
|
/* Return the number of instructions that must separate INSN1 and INSN2,
|
|
where INSN1 is the earlier instruction. Return the worst-case value
|
|
for any INSN2 if INSN2 is null. */
|
|
|
static unsigned int
|
static unsigned int
|
insns_between (const struct mips_cl_insn *insn1,
|
insns_between (const struct mips_cl_insn *insn1,
|
const struct mips_cl_insn *insn2)
|
const struct mips_cl_insn *insn2)
|
{
|
{
|
unsigned long pinfo1, pinfo2;
|
unsigned long pinfo1, pinfo2;
|
|
unsigned int mask;
|
|
|
/* This function needs to know which pinfo flags are set for INSN2
|
/* This function needs to know which pinfo flags are set for INSN2
|
and which registers INSN2 uses. The former is stored in PINFO2 and
|
and which registers INSN2 uses. The former is stored in PINFO2 and
|
the latter is tested via INSN2_USES_REG. If INSN2 is null, PINFO2
|
the latter is tested via INSN2_USES_GPR. If INSN2 is null, PINFO2
|
will have every flag set and INSN2_USES_REG will always return true. */
|
will have every flag set and INSN2_USES_GPR will always return true. */
|
pinfo1 = insn1->insn_mo->pinfo;
|
pinfo1 = insn1->insn_mo->pinfo;
|
pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
|
pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
|
|
|
#define INSN2_USES_REG(REG, CLASS) \
|
#define INSN2_USES_GPR(REG) \
|
(insn2 == NULL || insn_uses_reg (insn2, REG, CLASS))
|
(insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
|
|
|
/* For most targets, write-after-read dependencies on the HI and LO
|
/* For most targets, write-after-read dependencies on the HI and LO
|
registers must be separated by at least two instructions. */
|
registers must be separated by at least two instructions. */
|
if (!hilo_interlocks)
|
if (!hilo_interlocks)
|
{
|
{
|
Line 2532... |
Line 3203... |
}
|
}
|
|
|
/* If we're working around r7000 errata, there must be two instructions
|
/* If we're working around r7000 errata, there must be two instructions
|
between an mfhi or mflo and any instruction that uses the result. */
|
between an mfhi or mflo and any instruction that uses the result. */
|
if (mips_7000_hilo_fix
|
if (mips_7000_hilo_fix
|
|
&& !mips_opts.micromips
|
&& MF_HILO_INSN (pinfo1)
|
&& MF_HILO_INSN (pinfo1)
|
&& INSN2_USES_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG))
|
&& INSN2_USES_GPR (EXTRACT_OPERAND (0, RD, *insn1)))
|
return 2;
|
return 2;
|
|
|
/* If we're working around 24K errata, one instruction is required
|
/* If we're working around 24K errata, one instruction is required
|
if an ERET or DERET is followed by a branch instruction. */
|
if an ERET or DERET is followed by a branch instruction. */
|
if (mips_fix_24k)
|
if (mips_fix_24k && !mips_opts.micromips)
|
{
|
{
|
if (insn1->insn_opcode == INSN_ERET
|
if (insn1->insn_opcode == INSN_ERET
|
|| insn1->insn_opcode == INSN_DERET)
|
|| insn1->insn_opcode == INSN_DERET)
|
{
|
{
|
if (insn2 == NULL
|
if (insn2 == NULL
|
|| insn2->insn_opcode == INSN_ERET
|
|| insn2->insn_opcode == INSN_ERET
|
|| insn2->insn_opcode == INSN_DERET
|
|| insn2->insn_opcode == INSN_DERET
|
|| (insn2->insn_mo->pinfo
|
|| delayed_branch_p (insn2))
|
& (INSN_UNCOND_BRANCH_DELAY
|
|
| INSN_COND_BRANCH_DELAY
|
|
| INSN_COND_BRANCH_LIKELY)) != 0)
|
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
|
|
/* If working around VR4120 errata, check for combinations that need
|
/* If working around VR4120 errata, check for combinations that need
|
a single intervening instruction. */
|
a single intervening instruction. */
|
if (mips_fix_vr4120)
|
if (mips_fix_vr4120 && !mips_opts.micromips)
|
{
|
{
|
unsigned int class1, class2;
|
unsigned int class1, class2;
|
|
|
class1 = classify_vr4120_insn (insn1->insn_mo->name);
|
class1 = classify_vr4120_insn (insn1->insn_mo->name);
|
if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
|
if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
|
Line 2571... |
Line 3240... |
if (vr4120_conflicts[class1] & (1 << class2))
|
if (vr4120_conflicts[class1] & (1 << class2))
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
|
|
if (!mips_opts.mips16)
|
if (!HAVE_CODE_COMPRESSION)
|
{
|
{
|
/* Check for GPR or coprocessor load delays. All such delays
|
/* Check for GPR or coprocessor load delays. All such delays
|
are on the RT register. */
|
are on the RT register. */
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
|
if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
|
|| (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
|
|| (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
|
{
|
{
|
know (pinfo1 & INSN_WRITE_GPR_T);
|
know (pinfo1 & INSN_WRITE_GPR_T);
|
if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG))
|
if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Check for generic coprocessor hazards.
|
/* Check for generic coprocessor hazards.
|
|
|
Line 2598... |
Line 3267... |
|| (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
|
|| (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
|
{
|
{
|
/* Handle cases where INSN1 writes to a known general coprocessor
|
/* Handle cases where INSN1 writes to a known general coprocessor
|
register. There must be a one instruction delay before INSN2
|
register. There must be a one instruction delay before INSN2
|
if INSN2 reads that register, otherwise no delay is needed. */
|
if INSN2 reads that register, otherwise no delay is needed. */
|
if (pinfo1 & INSN_WRITE_FPR_T)
|
mask = fpr_write_mask (insn1);
|
|
if (mask != 0)
|
{
|
{
|
if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG))
|
if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
|
return 1;
|
|
}
|
|
else if (pinfo1 & INSN_WRITE_FPR_S)
|
|
{
|
|
if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG))
|
|
return 1;
|
return 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Read-after-write dependencies on the control registers
|
/* Read-after-write dependencies on the control registers
|
Line 2635... |
Line 3300... |
&& (pinfo1 & INSN_WRITE_COND_CODE)
|
&& (pinfo1 & INSN_WRITE_COND_CODE)
|
&& (pinfo2 & INSN_READ_COND_CODE))
|
&& (pinfo2 & INSN_READ_COND_CODE))
|
return 1;
|
return 1;
|
}
|
}
|
|
|
#undef INSN2_USES_REG
|
#undef INSN2_USES_GPR
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return the number of nops that would be needed to work around the
|
/* Return the number of nops that would be needed to work around the
|
VR4130 mflo/mfhi errata if instruction INSN immediately followed
|
VR4130 mflo/mfhi errata if instruction INSN immediately followed
|
the MAX_VR4130_NOPS instructions described by HIST. */
|
the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
|
|
that are contained within the first IGNORE instructions of HIST. */
|
|
|
static int
|
static int
|
nops_for_vr4130 (const struct mips_cl_insn *hist,
|
nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
|
const struct mips_cl_insn *insn)
|
const struct mips_cl_insn *insn)
|
{
|
{
|
int i, j, reg;
|
int i, j;
|
|
unsigned int mask;
|
|
|
/* Check if the instruction writes to HI or LO. MTHI and MTLO
|
/* Check if the instruction writes to HI or LO. MTHI and MTLO
|
are not affected by the errata. */
|
are not affected by the errata. */
|
if (insn != 0
|
if (insn != 0
|
&& ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
|
&& ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
|
Line 2663... |
Line 3330... |
/* Search for the first MFLO or MFHI. */
|
/* Search for the first MFLO or MFHI. */
|
for (i = 0; i < MAX_VR4130_NOPS; i++)
|
for (i = 0; i < MAX_VR4130_NOPS; i++)
|
if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
|
if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
|
{
|
{
|
/* Extract the destination register. */
|
/* Extract the destination register. */
|
if (mips_opts.mips16)
|
mask = gpr_write_mask (&hist[i]);
|
reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, hist[i])];
|
|
else
|
|
reg = EXTRACT_OPERAND (RD, hist[i]);
|
|
|
|
/* No nops are needed if INSN reads that register. */
|
/* No nops are needed if INSN reads that register. */
|
if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
|
if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
|
return 0;
|
return 0;
|
|
|
/* ...or if any of the intervening instructions do. */
|
/* ...or if any of the intervening instructions do. */
|
for (j = 0; j < i; j++)
|
for (j = 0; j < i; j++)
|
if (insn_uses_reg (&hist[j], reg, MIPS_GR_REG))
|
if (gpr_read_mask (&hist[j]) & mask)
|
return 0;
|
return 0;
|
|
|
|
if (i >= ignore)
|
return MAX_VR4130_NOPS - i;
|
return MAX_VR4130_NOPS - i;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
Line 2747... |
Line 3412... |
stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
|
stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
|
stinfo->align_to = fix_24k_align_to (insn->insn_mo);
|
stinfo->align_to = fix_24k_align_to (insn->insn_mo);
|
return TRUE;
|
return TRUE;
|
}
|
}
|
|
|
/* 24K Errata: Lost Data on Stores During Refill.
|
/* Return the number of nops that would be needed to work around the 24k
|
|
"lost data on stores during refill" errata if instruction INSN
|
|
immediately followed the 2 instructions described by HIST.
|
|
Ignore hazards that are contained within the first IGNORE
|
|
instructions of HIST.
|
|
|
Problem: The FSB (fetch store buffer) acts as an intermediate buffer
|
Problem: The FSB (fetch store buffer) acts as an intermediate buffer
|
for the data cache refills and store data. The following describes
|
for the data cache refills and store data. The following describes
|
the scenario where the store data could be lost.
|
the scenario where the store data could be lost.
|
|
|
Line 2787... |
Line 3456... |
* Run the data cache in write-through mode.
|
* Run the data cache in write-through mode.
|
* Insert a non-store instruction between
|
* Insert a non-store instruction between
|
Store A and Store B or Store B and Store C. */
|
Store A and Store B or Store B and Store C. */
|
|
|
static int
|
static int
|
nops_for_24k (const struct mips_cl_insn *hist,
|
nops_for_24k (int ignore, const struct mips_cl_insn *hist,
|
const struct mips_cl_insn *insn)
|
const struct mips_cl_insn *insn)
|
{
|
{
|
struct fix_24k_store_info pos[3];
|
struct fix_24k_store_info pos[3];
|
int align, i, base_offset;
|
int align, i, base_offset;
|
|
|
/* If INSN is definitely not a store, there's nothing to worry about. */
|
if (ignore >= 2)
|
if (insn && (insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
|
|
return 0;
|
return 0;
|
|
|
/* Likewise, the previous instruction wasn't a store. */
|
/* If the previous instruction wasn't a store, there's nothing to
|
|
worry about. */
|
if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
|
if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
|
return 0;
|
return 0;
|
|
|
/* If we don't know what came before, assume the worst. */
|
/* If the instructions after the previous one are unknown, we have
|
if (hist[1].frag == NULL)
|
to assume the worst. */
|
|
if (!insn)
|
return 1;
|
return 1;
|
|
|
/* If the instruction was not a store, there's nothing to worry about. */
|
/* Check whether we are dealing with three consecutive stores. */
|
if ((hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
|
if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
|
|
|| (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
|
return 0;
|
return 0;
|
|
|
/* If we don't know the relationship between the store addresses,
|
/* If we don't know the relationship between the store addresses,
|
assume the worst. */
|
assume the worst. */
|
if (insn == NULL
|
if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
|
|| !BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
|
|
|| !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
|
|| !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
|
return 1;
|
return 1;
|
|
|
if (!fix_24k_record_store_info (&pos[0], insn)
|
if (!fix_24k_record_store_info (&pos[0], insn)
|
|| !fix_24k_record_store_info (&pos[1], &hist[0])
|
|| !fix_24k_record_store_info (&pos[1], &hist[0])
|
Line 2867... |
Line 3537... |
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return the number of nops that would be needed if instruction INSN
|
/* Return the number of nops that would be needed if instruction INSN
|
immediately followed the MAX_NOPS instructions given by HIST,
|
immediately followed the MAX_NOPS instructions given by HIST,
|
where HIST[0] is the most recent instruction. If INSN is null,
|
where HIST[0] is the most recent instruction. Ignore hazards
|
return the worse-case number of nops for any instruction. */
|
between INSN and the first IGNORE instructions in HIST.
|
|
|
|
If INSN is null, return the worse-case number of nops for any
|
|
instruction. */
|
|
|
static int
|
static int
|
nops_for_insn (const struct mips_cl_insn *hist,
|
nops_for_insn (int ignore, const struct mips_cl_insn *hist,
|
const struct mips_cl_insn *insn)
|
const struct mips_cl_insn *insn)
|
{
|
{
|
int i, nops, tmp_nops;
|
int i, nops, tmp_nops;
|
|
|
nops = 0;
|
nops = 0;
|
for (i = 0; i < MAX_DELAY_NOPS; i++)
|
for (i = ignore; i < MAX_DELAY_NOPS; i++)
|
{
|
{
|
tmp_nops = insns_between (hist + i, insn) - i;
|
tmp_nops = insns_between (hist + i, insn) - i;
|
if (tmp_nops > nops)
|
if (tmp_nops > nops)
|
nops = tmp_nops;
|
nops = tmp_nops;
|
}
|
}
|
|
|
if (mips_fix_vr4130)
|
if (mips_fix_vr4130 && !mips_opts.micromips)
|
{
|
{
|
tmp_nops = nops_for_vr4130 (hist, insn);
|
tmp_nops = nops_for_vr4130 (ignore, hist, insn);
|
if (tmp_nops > nops)
|
if (tmp_nops > nops)
|
nops = tmp_nops;
|
nops = tmp_nops;
|
}
|
}
|
|
|
if (mips_fix_24k)
|
if (mips_fix_24k && !mips_opts.micromips)
|
{
|
{
|
tmp_nops = nops_for_24k (hist, insn);
|
tmp_nops = nops_for_24k (ignore, hist, insn);
|
if (tmp_nops > nops)
|
if (tmp_nops > nops)
|
nops = tmp_nops;
|
nops = tmp_nops;
|
}
|
}
|
|
|
return nops;
|
return nops;
|
}
|
}
|
|
|
/* The variable arguments provide NUM_INSNS extra instructions that
|
/* The variable arguments provide NUM_INSNS extra instructions that
|
might be added to HIST. Return the largest number of nops that
|
might be added to HIST. Return the largest number of nops that
|
would be needed after the extended sequence. */
|
would be needed after the extended sequence, ignoring hazards
|
|
in the first IGNORE instructions. */
|
|
|
static int
|
static int
|
nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
|
nops_for_sequence (int num_insns, int ignore,
|
|
const struct mips_cl_insn *hist, ...)
|
{
|
{
|
va_list args;
|
va_list args;
|
struct mips_cl_insn buffer[MAX_NOPS];
|
struct mips_cl_insn buffer[MAX_NOPS];
|
struct mips_cl_insn *cursor;
|
struct mips_cl_insn *cursor;
|
int nops;
|
int nops;
|
Line 2919... |
Line 3594... |
cursor = buffer + num_insns;
|
cursor = buffer + num_insns;
|
memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
|
memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
|
while (cursor > buffer)
|
while (cursor > buffer)
|
*--cursor = *va_arg (args, const struct mips_cl_insn *);
|
*--cursor = *va_arg (args, const struct mips_cl_insn *);
|
|
|
nops = nops_for_insn (buffer, NULL);
|
nops = nops_for_insn (ignore, buffer, NULL);
|
va_end (args);
|
va_end (args);
|
return nops;
|
return nops;
|
}
|
}
|
|
|
/* Like nops_for_insn, but if INSN is a branch, take into account the
|
/* Like nops_for_insn, but if INSN is a branch, take into account the
|
worst-case delay for the branch target. */
|
worst-case delay for the branch target. */
|
|
|
static int
|
static int
|
nops_for_insn_or_target (const struct mips_cl_insn *hist,
|
nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
|
const struct mips_cl_insn *insn)
|
const struct mips_cl_insn *insn)
|
{
|
{
|
int nops, tmp_nops;
|
int nops, tmp_nops;
|
|
|
nops = nops_for_insn (hist, insn);
|
nops = nops_for_insn (ignore, hist, insn);
|
if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
|
if (delayed_branch_p (insn))
|
| INSN_COND_BRANCH_DELAY
|
|
| INSN_COND_BRANCH_LIKELY))
|
|
{
|
{
|
tmp_nops = nops_for_sequence (2, hist, insn, NOP_INSN);
|
tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
|
|
hist, insn, get_delay_slot_nop (insn));
|
if (tmp_nops > nops)
|
if (tmp_nops > nops)
|
nops = tmp_nops;
|
nops = tmp_nops;
|
}
|
}
|
else if (mips_opts.mips16
|
else if (compact_branch_p (insn))
|
&& (insn->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
|
|
| MIPS16_INSN_COND_BRANCH)))
|
|
{
|
{
|
tmp_nops = nops_for_sequence (1, hist, insn);
|
tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
|
if (tmp_nops > nops)
|
if (tmp_nops > nops)
|
nops = tmp_nops;
|
nops = tmp_nops;
|
}
|
}
|
return nops;
|
return nops;
|
}
|
}
|
Line 2958... |
Line 3630... |
/* Fix NOP issue: Replace nops by "or at,at,zero". */
|
/* Fix NOP issue: Replace nops by "or at,at,zero". */
|
|
|
static void
|
static void
|
fix_loongson2f_nop (struct mips_cl_insn * ip)
|
fix_loongson2f_nop (struct mips_cl_insn * ip)
|
{
|
{
|
|
gas_assert (!HAVE_CODE_COMPRESSION);
|
if (strcmp (ip->insn_mo->name, "nop") == 0)
|
if (strcmp (ip->insn_mo->name, "nop") == 0)
|
ip->insn_opcode = LOONGSON2F_NOP_INSN;
|
ip->insn_opcode = LOONGSON2F_NOP_INSN;
|
}
|
}
|
|
|
/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
|
/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
|
jr target pc &= 'hffff_ffff_cfff_ffff. */
|
jr target pc &= 'hffff_ffff_cfff_ffff. */
|
|
|
static void
|
static void
|
fix_loongson2f_jump (struct mips_cl_insn * ip)
|
fix_loongson2f_jump (struct mips_cl_insn * ip)
|
{
|
{
|
|
gas_assert (!HAVE_CODE_COMPRESSION);
|
if (strcmp (ip->insn_mo->name, "j") == 0
|
if (strcmp (ip->insn_mo->name, "j") == 0
|
|| strcmp (ip->insn_mo->name, "jr") == 0
|
|| strcmp (ip->insn_mo->name, "jr") == 0
|
|| strcmp (ip->insn_mo->name, "jalr") == 0)
|
|| strcmp (ip->insn_mo->name, "jalr") == 0)
|
{
|
{
|
int sreg;
|
int sreg;
|
expressionS ep;
|
expressionS ep;
|
|
|
if (! mips_opts.at)
|
if (! mips_opts.at)
|
return;
|
return;
|
|
|
sreg = EXTRACT_OPERAND (RS, *ip);
|
sreg = EXTRACT_OPERAND (0, RS, *ip);
|
if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
|
if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
|
return;
|
return;
|
|
|
ep.X_op = O_constant;
|
ep.X_op = O_constant;
|
ep.X_add_number = 0xcfff0000;
|
ep.X_add_number = 0xcfff0000;
|
Line 3001... |
Line 3675... |
|
|
if (mips_fix_loongson2f_jump)
|
if (mips_fix_loongson2f_jump)
|
fix_loongson2f_jump (ip);
|
fix_loongson2f_jump (ip);
|
}
|
}
|
|
|
/* Output an instruction. IP is the instruction information.
|
/* IP is a branch that has a delay slot, and we need to fill it
|
ADDRESS_EXPR is an operand of the instruction to be used with
|
automatically. Return true if we can do that by swapping IP
|
RELOC_TYPE. */
|
with the previous instruction. */
|
|
|
static void
|
static bfd_boolean
|
append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
can_swap_branch_p (struct mips_cl_insn *ip)
|
bfd_reloc_code_real_type *reloc_type)
|
|
{
|
{
|
unsigned long prev_pinfo, pinfo;
|
unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
|
unsigned long prev_pinfo2, pinfo2;
|
unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
|
relax_stateT prev_insn_frag_type = 0;
|
|
bfd_boolean relaxed_branch = FALSE;
|
|
segment_info_type *si = seg_info (now_seg);
|
|
|
|
if (mips_fix_loongson2f)
|
|
fix_loongson2f (ip);
|
|
|
|
/* Mark instruction labels in mips16 mode. */
|
/* -O2 and above is required for this optimization. */
|
mips16_mark_labels ();
|
if (mips_optimize < 2)
|
|
return FALSE;
|
|
|
file_ase_mips16 |= mips_opts.mips16;
|
/* If we have seen .set volatile or .set nomove, don't optimize. */
|
|
if (mips_opts.nomove)
|
|
return FALSE;
|
|
|
prev_pinfo = history[0].insn_mo->pinfo;
|
/* We can't swap if the previous instruction's position is fixed. */
|
prev_pinfo2 = history[0].insn_mo->pinfo2;
|
if (history[0].fixed_p)
|
pinfo = ip->insn_mo->pinfo;
|
return FALSE;
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
|
|
if (address_expr == NULL)
|
/* If the previous previous insn was in a .set noreorder, we can't
|
ip->complete_p = 1;
|
swap. Actually, the MIPS assembler will swap in this situation.
|
else if (*reloc_type <= BFD_RELOC_UNUSED
|
However, gcc configured -with-gnu-as will generate code like
|
&& address_expr->X_op == O_constant)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
ip->complete_p = 1;
|
.set noreorder
|
switch (*reloc_type)
|
lw $4,XXX
|
{
|
.set reorder
|
case BFD_RELOC_32:
|
INSN
|
ip->insn_opcode |= address_expr->X_add_number;
|
bne $4,$0,foo
|
break;
|
|
|
|
case BFD_RELOC_MIPS_HIGHEST:
|
in which we can not swap the bne and INSN. If gcc is not configured
|
tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
|
-with-gnu-as, it does not output the .set pseudo-ops. */
|
ip->insn_opcode |= tmp & 0xffff;
|
if (history[1].noreorder_p)
|
break;
|
return FALSE;
|
|
|
case BFD_RELOC_MIPS_HIGHER:
|
/* If the previous instruction had a fixup in mips16 mode, we can not swap.
|
tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
|
This means that the previous instruction was a 4-byte one anyhow. */
|
ip->insn_opcode |= tmp & 0xffff;
|
if (mips_opts.mips16 && history[0].fixp[0])
|
break;
|
return FALSE;
|
|
|
case BFD_RELOC_HI16_S:
|
/* If the branch is itself the target of a branch, we can not swap.
|
tmp = (address_expr->X_add_number + 0x8000) >> 16;
|
We cheat on this; all we check for is whether there is a label on
|
ip->insn_opcode |= tmp & 0xffff;
|
this instruction. If there are any branches to anything other than
|
break;
|
a label, users must use .set noreorder. */
|
|
if (seg_info (now_seg)->label_list)
|
|
return FALSE;
|
|
|
case BFD_RELOC_HI16:
|
/* If the previous instruction is in a variant frag other than this
|
ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
|
branch's one, we cannot do the swap. This does not apply to
|
break;
|
MIPS16/microMIPS code, which uses variant frags for different
|
|
purposes. */
|
|
if (!HAVE_CODE_COMPRESSION
|
|
&& history[0].frag
|
|
&& history[0].frag->fr_type == rs_machine_dependent)
|
|
return FALSE;
|
|
|
case BFD_RELOC_UNUSED:
|
/* We do not swap with instructions that cannot architecturally
|
case BFD_RELOC_LO16:
|
be placed in a branch delay slot, such as SYNC or ERET. We
|
case BFD_RELOC_MIPS_GOT_DISP:
|
also refrain from swapping with a trap instruction, since it
|
ip->insn_opcode |= address_expr->X_add_number & 0xffff;
|
complicates trap handlers to have the trap instruction be in
|
break;
|
a delay slot. */
|
|
prev_pinfo = history[0].insn_mo->pinfo;
|
|
if (prev_pinfo & INSN_NO_DELAY_SLOT)
|
|
return FALSE;
|
|
|
case BFD_RELOC_MIPS_JMP:
|
/* Check for conflicts between the branch and the instructions
|
if ((address_expr->X_add_number & 3) != 0)
|
before the candidate delay slot. */
|
as_bad (_("jump to misaligned address (0x%lx)"),
|
if (nops_for_insn (0, history + 1, ip) > 0)
|
(unsigned long) address_expr->X_add_number);
|
return FALSE;
|
ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
|
|
ip->complete_p = 0;
|
|
break;
|
|
|
|
case BFD_RELOC_MIPS16_JMP:
|
/* Check for conflicts between the swapped sequence and the
|
if ((address_expr->X_add_number & 3) != 0)
|
target of the branch. */
|
as_bad (_("jump to misaligned address (0x%lx)"),
|
if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
|
(unsigned long) address_expr->X_add_number);
|
return FALSE;
|
ip->insn_opcode |=
|
|
(((address_expr->X_add_number & 0x7c0000) << 3)
|
|
| ((address_expr->X_add_number & 0xf800000) >> 7)
|
|
| ((address_expr->X_add_number & 0x3fffc) >> 2));
|
|
ip->complete_p = 0;
|
|
break;
|
|
|
|
case BFD_RELOC_16_PCREL_S2:
|
/* If the branch reads a register that the previous
|
if ((address_expr->X_add_number & 3) != 0)
|
instruction sets, we can not swap. */
|
as_bad (_("branch to misaligned address (0x%lx)"),
|
gpr_read = gpr_read_mask (ip);
|
(unsigned long) address_expr->X_add_number);
|
prev_gpr_write = gpr_write_mask (&history[0]);
|
if (mips_relax_branch)
|
if (gpr_read & prev_gpr_write)
|
goto need_reloc;
|
return FALSE;
|
if ((address_expr->X_add_number + 0x20000) & ~0x3ffff)
|
|
as_bad (_("branch address range overflow (0x%lx)"),
|
|
(unsigned long) address_expr->X_add_number);
|
|
ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff;
|
|
ip->complete_p = 0;
|
|
break;
|
|
|
|
default:
|
/* If the branch writes a register that the previous
|
internalError ();
|
instruction sets, we can not swap. */
|
}
|
gpr_write = gpr_write_mask (ip);
|
|
if (gpr_write & prev_gpr_write)
|
|
return FALSE;
|
|
|
|
/* If the branch writes a register that the previous
|
|
instruction reads, we can not swap. */
|
|
prev_gpr_read = gpr_read_mask (&history[0]);
|
|
if (gpr_write & prev_gpr_read)
|
|
return FALSE;
|
|
|
|
/* If one instruction sets a condition code and the
|
|
other one uses a condition code, we can not swap. */
|
|
pinfo = ip->insn_mo->pinfo;
|
|
if ((pinfo & INSN_READ_COND_CODE)
|
|
&& (prev_pinfo & INSN_WRITE_COND_CODE))
|
|
return FALSE;
|
|
if ((pinfo & INSN_WRITE_COND_CODE)
|
|
&& (prev_pinfo & INSN_READ_COND_CODE))
|
|
return FALSE;
|
|
|
|
/* If the previous instruction uses the PC, we can not swap. */
|
|
prev_pinfo2 = history[0].insn_mo->pinfo2;
|
|
if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
|
|
return FALSE;
|
|
if (mips_opts.micromips && (prev_pinfo2 & INSN2_READ_PC))
|
|
return FALSE;
|
|
|
|
/* If the previous instruction has an incorrect size for a fixed
|
|
branch delay slot in microMIPS mode, we cannot swap. */
|
|
pinfo2 = ip->insn_mo->pinfo2;
|
|
if (mips_opts.micromips
|
|
&& (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
|
|
&& insn_length (history) != 2)
|
|
return FALSE;
|
|
if (mips_opts.micromips
|
|
&& (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
|
|
&& insn_length (history) != 4)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
}
|
}
|
|
|
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
|
/* Decide how we should add IP to the instruction stream. */
|
|
|
|
static enum append_method
|
|
get_append_method (struct mips_cl_insn *ip)
|
{
|
{
|
/* There are a lot of optimizations we could do that we don't.
|
unsigned long pinfo;
|
In particular, we do not, in general, reorder instructions.
|
|
|
/* The relaxed version of a macro sequence must be inherently
|
|
hazard-free. */
|
|
if (mips_relax.sequence == 2)
|
|
return APPEND_ADD;
|
|
|
|
/* We must not dabble with instructions in a ".set norerorder" block. */
|
|
if (mips_opts.noreorder)
|
|
return APPEND_ADD;
|
|
|
|
/* Otherwise, it's our responsibility to fill branch delay slots. */
|
|
if (delayed_branch_p (ip))
|
|
{
|
|
if (!branch_likely_p (ip) && can_swap_branch_p (ip))
|
|
return APPEND_SWAP;
|
|
|
|
pinfo = ip->insn_mo->pinfo;
|
|
if (mips_opts.mips16
|
|
&& ISA_SUPPORTS_MIPS16E
|
|
&& (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
|
|
return APPEND_ADD_COMPACT;
|
|
|
|
return APPEND_ADD_WITH_NOP;
|
|
}
|
|
|
|
return APPEND_ADD;
|
|
}
|
|
|
|
/* IP is a MIPS16 instruction whose opcode we have just changed.
|
|
Point IP->insn_mo to the new opcode's definition. */
|
|
|
|
static void
|
|
find_altered_mips16_opcode (struct mips_cl_insn *ip)
|
|
{
|
|
const struct mips_opcode *mo, *end;
|
|
|
|
end = &mips16_opcodes[bfd_mips16_num_opcodes];
|
|
for (mo = ip->insn_mo; mo < end; mo++)
|
|
if ((ip->insn_opcode & mo->mask) == mo->match)
|
|
{
|
|
ip->insn_mo = mo;
|
|
return;
|
|
}
|
|
abort ();
|
|
}
|
|
|
|
/* For microMIPS macros, we need to generate a local number label
|
|
as the target of branches. */
|
|
#define MICROMIPS_LABEL_CHAR '\037'
|
|
static unsigned long micromips_target_label;
|
|
static char micromips_target_name[32];
|
|
|
|
static char *
|
|
micromips_label_name (void)
|
|
{
|
|
char *p = micromips_target_name;
|
|
char symbol_name_temporary[24];
|
|
unsigned long l;
|
|
int i;
|
|
|
|
if (*p)
|
|
return p;
|
|
|
|
i = 0;
|
|
l = micromips_target_label;
|
|
#ifdef LOCAL_LABEL_PREFIX
|
|
*p++ = LOCAL_LABEL_PREFIX;
|
|
#endif
|
|
*p++ = 'L';
|
|
*p++ = MICROMIPS_LABEL_CHAR;
|
|
do
|
|
{
|
|
symbol_name_temporary[i++] = l % 10 + '0';
|
|
l /= 10;
|
|
}
|
|
while (l != 0);
|
|
while (i > 0)
|
|
*p++ = symbol_name_temporary[--i];
|
|
*p = '\0';
|
|
|
|
return micromips_target_name;
|
|
}
|
|
|
|
static void
|
|
micromips_label_expr (expressionS *label_expr)
|
|
{
|
|
label_expr->X_op = O_symbol;
|
|
label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
|
|
label_expr->X_add_number = 0;
|
|
}
|
|
|
|
static void
|
|
micromips_label_inc (void)
|
|
{
|
|
micromips_target_label++;
|
|
*micromips_target_name = '\0';
|
|
}
|
|
|
|
static void
|
|
micromips_add_label (void)
|
|
{
|
|
symbolS *s;
|
|
|
|
s = colon (micromips_label_name ());
|
|
micromips_label_inc ();
|
|
#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
|
|
if (IS_ELF)
|
|
S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
|
|
#endif
|
|
}
|
|
|
|
/* If assembling microMIPS code, then return the microMIPS reloc
|
|
corresponding to the requested one if any. Otherwise return
|
|
the reloc unchanged. */
|
|
|
|
static bfd_reloc_code_real_type
|
|
micromips_map_reloc (bfd_reloc_code_real_type reloc)
|
|
{
|
|
static const bfd_reloc_code_real_type relocs[][2] =
|
|
{
|
|
/* Keep sorted incrementally by the left-hand key. */
|
|
{ BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
|
|
{ BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
|
|
{ BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
|
|
{ BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
|
|
{ BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
|
|
{ BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
|
|
{ BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
|
|
{ BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
|
|
{ BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
|
|
{ BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
|
|
{ BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
|
|
{ BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
|
|
{ BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
|
|
{ BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
|
|
{ BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
|
|
{ BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
|
|
{ BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
|
|
{ BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
|
|
{ BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
|
|
{ BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
|
|
{ BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
|
|
{ BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
|
|
{ BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
|
|
{ BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
|
|
{ BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
|
|
{ BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
|
|
{ BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
|
|
};
|
|
bfd_reloc_code_real_type r;
|
|
size_t i;
|
|
|
|
if (!mips_opts.micromips)
|
|
return reloc;
|
|
for (i = 0; i < ARRAY_SIZE (relocs); i++)
|
|
{
|
|
r = relocs[i][0];
|
|
if (r > reloc)
|
|
return reloc;
|
|
if (r == reloc)
|
|
return relocs[i][1];
|
|
}
|
|
return reloc;
|
|
}
|
|
|
|
/* Output an instruction. IP is the instruction information.
|
|
ADDRESS_EXPR is an operand of the instruction to be used with
|
|
RELOC_TYPE. EXPANSIONP is true if the instruction is part of
|
|
a macro expansion. */
|
|
|
|
static void
|
|
append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|
bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
|
|
{
|
|
unsigned long prev_pinfo2, pinfo;
|
|
bfd_boolean relaxed_branch = FALSE;
|
|
enum append_method method;
|
|
bfd_boolean relax32;
|
|
int branch_disp;
|
|
|
|
if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
|
|
fix_loongson2f (ip);
|
|
|
|
mips_mark_labels ();
|
|
|
|
file_ase_mips16 |= mips_opts.mips16;
|
|
file_ase_micromips |= mips_opts.micromips;
|
|
|
|
prev_pinfo2 = history[0].insn_mo->pinfo2;
|
|
pinfo = ip->insn_mo->pinfo;
|
|
|
|
if (mips_opts.micromips
|
|
&& !expansionp
|
|
&& (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
|
|
&& micromips_insn_length (ip->insn_mo) != 2)
|
|
|| ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
|
|
&& micromips_insn_length (ip->insn_mo) != 4)))
|
|
as_warn (_("Wrong size instruction in a %u-bit branch delay slot"),
|
|
(prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
|
|
|
|
if (address_expr == NULL)
|
|
ip->complete_p = 1;
|
|
else if (*reloc_type <= BFD_RELOC_UNUSED
|
|
&& address_expr->X_op == O_constant)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
ip->complete_p = 1;
|
|
switch (*reloc_type)
|
|
{
|
|
case BFD_RELOC_32:
|
|
ip->insn_opcode |= address_expr->X_add_number;
|
|
break;
|
|
|
|
case BFD_RELOC_MIPS_HIGHEST:
|
|
tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
|
|
ip->insn_opcode |= tmp & 0xffff;
|
|
break;
|
|
|
|
case BFD_RELOC_MIPS_HIGHER:
|
|
tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
|
|
ip->insn_opcode |= tmp & 0xffff;
|
|
break;
|
|
|
|
case BFD_RELOC_HI16_S:
|
|
tmp = (address_expr->X_add_number + 0x8000) >> 16;
|
|
ip->insn_opcode |= tmp & 0xffff;
|
|
break;
|
|
|
|
case BFD_RELOC_HI16:
|
|
ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
|
|
break;
|
|
|
|
case BFD_RELOC_UNUSED:
|
|
case BFD_RELOC_LO16:
|
|
case BFD_RELOC_MIPS_GOT_DISP:
|
|
ip->insn_opcode |= address_expr->X_add_number & 0xffff;
|
|
break;
|
|
|
|
case BFD_RELOC_MIPS_JMP:
|
|
{
|
|
int shift;
|
|
|
|
shift = mips_opts.micromips ? 1 : 2;
|
|
if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
|
|
as_bad (_("jump to misaligned address (0x%lx)"),
|
|
(unsigned long) address_expr->X_add_number);
|
|
ip->insn_opcode |= ((address_expr->X_add_number >> shift)
|
|
& 0x3ffffff);
|
|
ip->complete_p = 0;
|
|
}
|
|
break;
|
|
|
|
case BFD_RELOC_MIPS16_JMP:
|
|
if ((address_expr->X_add_number & 3) != 0)
|
|
as_bad (_("jump to misaligned address (0x%lx)"),
|
|
(unsigned long) address_expr->X_add_number);
|
|
ip->insn_opcode |=
|
|
(((address_expr->X_add_number & 0x7c0000) << 3)
|
|
| ((address_expr->X_add_number & 0xf800000) >> 7)
|
|
| ((address_expr->X_add_number & 0x3fffc) >> 2));
|
|
ip->complete_p = 0;
|
|
break;
|
|
|
|
case BFD_RELOC_16_PCREL_S2:
|
|
{
|
|
int shift;
|
|
|
|
shift = mips_opts.micromips ? 1 : 2;
|
|
if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
|
|
as_bad (_("branch to misaligned address (0x%lx)"),
|
|
(unsigned long) address_expr->X_add_number);
|
|
if (!mips_relax_branch)
|
|
{
|
|
if ((address_expr->X_add_number + (1 << (shift + 15)))
|
|
& ~((1 << (shift + 16)) - 1))
|
|
as_bad (_("branch address range overflow (0x%lx)"),
|
|
(unsigned long) address_expr->X_add_number);
|
|
ip->insn_opcode |= ((address_expr->X_add_number >> shift)
|
|
& 0xffff);
|
|
}
|
|
ip->complete_p = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
internalError ();
|
|
}
|
|
}
|
|
|
|
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
|
|
{
|
|
/* There are a lot of optimizations we could do that we don't.
|
|
In particular, we do not, in general, reorder instructions.
|
If you use gcc with optimization, it will reorder
|
If you use gcc with optimization, it will reorder
|
instructions and generally do much more optimization then we
|
instructions and generally do much more optimization then we
|
do here; repeating all that work in the assembler would only
|
do here; repeating all that work in the assembler would only
|
benefit hand written assembly code, and does not seem worth
|
benefit hand written assembly code, and does not seem worth
|
it. */
|
it. */
|
int nops = (mips_optimize == 0
|
int nops = (mips_optimize == 0
|
? nops_for_insn (history, NULL)
|
? nops_for_insn (0, history, NULL)
|
: nops_for_insn_or_target (history, ip));
|
: nops_for_insn_or_target (0, history, ip));
|
if (nops > 0)
|
if (nops > 0)
|
{
|
{
|
fragS *old_frag;
|
fragS *old_frag;
|
unsigned long old_frag_offset;
|
unsigned long old_frag_offset;
|
int i;
|
int i;
|
|
|
old_frag = frag_now;
|
old_frag = frag_now;
|
old_frag_offset = frag_now_fix ();
|
old_frag_offset = frag_now_fix ();
|
|
|
for (i = 0; i < nops; i++)
|
for (i = 0; i < nops; i++)
|
emit_nop ();
|
add_fixed_insn (NOP_INSN);
|
|
insert_into_history (0, nops, NOP_INSN);
|
|
|
if (listing)
|
if (listing)
|
{
|
{
|
listing_prev_line ();
|
listing_prev_line ();
|
/* We may be at the start of a variant frag. In case we
|
/* We may be at the start of a variant frag. In case we
|
Line 3152... |
Line 4149... |
#endif
|
#endif
|
}
|
}
|
}
|
}
|
else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
|
else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
|
{
|
{
|
/* Work out how many nops in prev_nop_frag are needed by IP. */
|
int nops;
|
int nops = nops_for_insn_or_target (history, ip);
|
|
|
/* Work out how many nops in prev_nop_frag are needed by IP,
|
|
ignoring hazards generated by the first prev_nop_frag_since
|
|
instructions. */
|
|
nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
|
gas_assert (nops <= prev_nop_frag_holds);
|
gas_assert (nops <= prev_nop_frag_holds);
|
|
|
/* Enforce NOPS as a minimum. */
|
/* Enforce NOPS as a minimum. */
|
if (nops > prev_nop_frag_required)
|
if (nops > prev_nop_frag_required)
|
prev_nop_frag_required = nops;
|
prev_nop_frag_required = nops;
|
Line 3172... |
Line 4173... |
}
|
}
|
else
|
else
|
{
|
{
|
/* Allow this instruction to replace one of the nops that was
|
/* Allow this instruction to replace one of the nops that was
|
tentatively added to prev_nop_frag. */
|
tentatively added to prev_nop_frag. */
|
prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
|
prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
|
prev_nop_frag_holds--;
|
prev_nop_frag_holds--;
|
prev_nop_frag_since++;
|
prev_nop_frag_since++;
|
}
|
}
|
}
|
}
|
|
|
|
method = get_append_method (ip);
|
|
branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
|
|
|
#ifdef OBJ_ELF
|
#ifdef OBJ_ELF
|
/* The value passed to dwarf2_emit_insn is the distance between
|
/* The value passed to dwarf2_emit_insn is the distance between
|
the beginning of the current instruction and the address that
|
the beginning of the current instruction and the address that
|
should be recorded in the debug tables. For MIPS16 debug info
|
should be recorded in the debug tables. This is normally the
|
we want to use ISA-encoded addresses, so we pass -1 for an
|
current address.
|
address higher by one than the current. */
|
|
dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0);
|
|
#endif
|
|
|
|
/* Record the frag type before frag_var. */
|
For MIPS16/microMIPS debug info we want to use ISA-encoded
|
if (history[0].frag)
|
addresses, so we use -1 for an address higher by one than the
|
prev_insn_frag_type = history[0].frag->fr_type;
|
current one.
|
|
|
|
If the instruction produced is a branch that we will swap with
|
|
the preceding instruction, then we add the displacement by which
|
|
the branch will be moved backwards. This is more appropriate
|
|
and for MIPS16/microMIPS code also prevents a debugger from
|
|
placing a breakpoint in the middle of the branch (and corrupting
|
|
code if software breakpoints are used). */
|
|
dwarf2_emit_insn ((HAVE_CODE_COMPRESSION ? -1 : 0) + branch_disp);
|
|
#endif
|
|
|
if (address_expr
|
relax32 = (mips_relax_branch
|
&& *reloc_type == BFD_RELOC_16_PCREL_S2
|
|
&& (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
|
|
|| pinfo & INSN_COND_BRANCH_LIKELY)
|
|
&& mips_relax_branch
|
|
/* Don't try branch relaxation within .set nomacro, or within
|
/* Don't try branch relaxation within .set nomacro, or within
|
.set noat if we use $at for PIC computations. If it turns
|
.set noat if we use $at for PIC computations. If it turns
|
out that the branch was out-of-range, we'll get an error. */
|
out that the branch was out-of-range, we'll get an error. */
|
&& !mips_opts.warn_about_macros
|
&& !mips_opts.warn_about_macros
|
&& (mips_opts.at || mips_pic == NO_PIC)
|
&& (mips_opts.at || mips_pic == NO_PIC)
|
/* Don't relax BPOSGE32/64 as they have no complementing branches. */
|
/* Don't relax BPOSGE32/64 as they have no complementing
|
&& !(ip->insn_mo->membership & (INSN_DSP64 | INSN_DSP))
|
branches. */
|
&& !mips_opts.mips16)
|
&& !(ip->insn_mo->membership & (INSN_DSP64 | INSN_DSP)));
|
|
|
|
if (!HAVE_CODE_COMPRESSION
|
|
&& address_expr
|
|
&& relax32
|
|
&& *reloc_type == BFD_RELOC_16_PCREL_S2
|
|
&& delayed_branch_p (ip))
|
{
|
{
|
relaxed_branch = TRUE;
|
relaxed_branch = TRUE;
|
add_relaxed_insn (ip, (relaxed_branch_length
|
add_relaxed_insn (ip, (relaxed_branch_length
|
(NULL, NULL,
|
(NULL, NULL,
|
(pinfo & INSN_UNCOND_BRANCH_DELAY) ? -1
|
uncond_branch_p (ip) ? -1
|
: (pinfo & INSN_COND_BRANCH_LIKELY) ? 1
|
: branch_likely_p (ip) ? 1
|
: 0)), 4,
|
: 0)), 4,
|
RELAX_BRANCH_ENCODE
|
RELAX_BRANCH_ENCODE
|
(AT,
|
(AT,
|
pinfo & INSN_UNCOND_BRANCH_DELAY,
|
uncond_branch_p (ip),
|
pinfo & INSN_COND_BRANCH_LIKELY,
|
branch_likely_p (ip),
|
pinfo & INSN_WRITE_GPR_31,
|
pinfo & INSN_WRITE_GPR_31,
|
0),
|
0),
|
address_expr->X_add_symbol,
|
address_expr->X_add_symbol,
|
address_expr->X_add_number);
|
address_expr->X_add_number);
|
*reloc_type = BFD_RELOC_UNUSED;
|
*reloc_type = BFD_RELOC_UNUSED;
|
}
|
}
|
else if (*reloc_type > BFD_RELOC_UNUSED)
|
else if (mips_opts.micromips
|
|
&& address_expr
|
|
&& ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
|
|
|| *reloc_type > BFD_RELOC_UNUSED)
|
|
&& (delayed_branch_p (ip) || compact_branch_p (ip))
|
|
/* Don't try branch relaxation when users specify
|
|
16-bit/32-bit instructions. */
|
|
&& !forced_insn_length)
|
|
{
|
|
bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
|
|
int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
|
|
int uncond = uncond_branch_p (ip) ? -1 : 0;
|
|
int compact = compact_branch_p (ip);
|
|
int al = pinfo & INSN_WRITE_GPR_31;
|
|
int length32;
|
|
|
|
gas_assert (address_expr != NULL);
|
|
gas_assert (!mips_relax.sequence);
|
|
|
|
relaxed_branch = TRUE;
|
|
length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
|
|
add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
|
|
RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
|
|
relax32, 0, 0),
|
|
address_expr->X_add_symbol,
|
|
address_expr->X_add_number);
|
|
*reloc_type = BFD_RELOC_UNUSED;
|
|
}
|
|
else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
|
{
|
{
|
/* We need to set up a variant frag. */
|
/* We need to set up a variant frag. */
|
gas_assert (mips_opts.mips16 && address_expr != NULL);
|
gas_assert (address_expr != NULL);
|
add_relaxed_insn (ip, 4, 0,
|
add_relaxed_insn (ip, 4, 0,
|
RELAX_MIPS16_ENCODE
|
RELAX_MIPS16_ENCODE
|
(*reloc_type - BFD_RELOC_UNUSED,
|
(*reloc_type - BFD_RELOC_UNUSED,
|
mips16_small, mips16_ext,
|
forced_insn_length == 2, forced_insn_length == 4,
|
prev_pinfo & INSN_UNCOND_BRANCH_DELAY,
|
delayed_branch_p (&history[0]),
|
history[0].mips16_absolute_jump_p),
|
history[0].mips16_absolute_jump_p),
|
make_expr_symbol (address_expr), 0);
|
make_expr_symbol (address_expr), 0);
|
}
|
}
|
else if (mips_opts.mips16
|
else if (mips_opts.mips16
|
&& ! ip->use_extend
|
&& ! ip->use_extend
|
&& *reloc_type != BFD_RELOC_MIPS16_JMP)
|
&& *reloc_type != BFD_RELOC_MIPS16_JMP)
|
{
|
{
|
if ((pinfo & INSN_UNCOND_BRANCH_DELAY) == 0)
|
if (!delayed_branch_p (ip))
|
/* Make sure there is enough room to swap this instruction with
|
/* Make sure there is enough room to swap this instruction with
|
a following jump instruction. */
|
a following jump instruction. */
|
frag_grow (6);
|
frag_grow (6);
|
add_fixed_insn (ip);
|
add_fixed_insn (ip);
|
}
|
}
|
else
|
else
|
{
|
{
|
if (mips_opts.mips16
|
if (mips_opts.mips16
|
&& mips_opts.noreorder
|
&& mips_opts.noreorder
|
&& (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
|
&& delayed_branch_p (&history[0]))
|
as_warn (_("extended instruction in delay slot"));
|
as_warn (_("extended instruction in delay slot"));
|
|
|
if (mips_relax.sequence)
|
if (mips_relax.sequence)
|
{
|
{
|
/* If we've reached the end of this frag, turn it into a variant
|
/* If we've reached the end of this frag, turn it into a variant
|
frag and record the information for the instructions we've
|
frag and record the information for the instructions we've
|
written so far. */
|
written so far. */
|
if (frag_room () < 4)
|
if (frag_room () < 4)
|
relax_close_frag ();
|
relax_close_frag ();
|
mips_relax.sizes[mips_relax.sequence - 1] += 4;
|
mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
|
}
|
}
|
|
|
if (mips_relax.sequence != 2)
|
if (mips_relax.sequence != 2)
|
mips_macro_warning.sizes[0] += 4;
|
{
|
|
if (mips_macro_warning.first_insn_sizes[0] == 0)
|
|
mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
|
|
mips_macro_warning.sizes[0] += insn_length (ip);
|
|
mips_macro_warning.insns[0]++;
|
|
}
|
if (mips_relax.sequence != 1)
|
if (mips_relax.sequence != 1)
|
mips_macro_warning.sizes[1] += 4;
|
{
|
|
if (mips_macro_warning.first_insn_sizes[1] == 0)
|
|
mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
|
|
mips_macro_warning.sizes[1] += insn_length (ip);
|
|
mips_macro_warning.insns[1]++;
|
|
}
|
|
|
if (mips_opts.mips16)
|
if (mips_opts.mips16)
|
{
|
{
|
ip->fixed_p = 1;
|
ip->fixed_p = 1;
|
ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
|
ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
|
}
|
}
|
add_fixed_insn (ip);
|
add_fixed_insn (ip);
|
}
|
}
|
|
|
if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED)
|
if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
|
{
|
|
if (!ip->complete_p
|
|
&& *reloc_type < BFD_RELOC_UNUSED)
|
|
need_reloc:
|
|
{
|
{
|
|
bfd_reloc_code_real_type final_type[3];
|
|
reloc_howto_type *howto0;
|
reloc_howto_type *howto;
|
reloc_howto_type *howto;
|
int i;
|
int i;
|
|
|
|
/* Perform any necessary conversion to microMIPS relocations
|
|
and find out how many relocations there actually are. */
|
|
for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
|
|
final_type[i] = micromips_map_reloc (reloc_type[i]);
|
|
|
/* In a compound relocation, it is the final (outermost)
|
/* In a compound relocation, it is the final (outermost)
|
operator that determines the relocated field. */
|
operator that determines the relocated field. */
|
for (i = 1; i < 3; i++)
|
howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
|
if (reloc_type[i] == BFD_RELOC_UNUSED)
|
|
break;
|
|
|
|
howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
|
|
if (howto == NULL)
|
if (howto == NULL)
|
{
|
{
|
/* To reproduce this failure try assembling gas/testsuites/
|
/* To reproduce this failure try assembling gas/testsuites/
|
gas/mips/mips16-intermix.s with a mips-ecoff targeted
|
gas/mips/mips16-intermix.s with a mips-ecoff targeted
|
assembler. */
|
assembler. */
|
as_bad (_("Unsupported MIPS relocation number %d"), reloc_type[i - 1]);
|
as_bad (_("Unsupported MIPS relocation number %d"),
|
|
final_type[i - 1]);
|
howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
|
howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
|
}
|
}
|
|
|
|
if (i > 1)
|
|
howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
|
ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
|
ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
|
bfd_get_reloc_size (howto),
|
bfd_get_reloc_size (howto),
|
address_expr,
|
address_expr,
|
reloc_type[0] == BFD_RELOC_16_PCREL_S2,
|
howto0 && howto0->pc_relative,
|
reloc_type[0]);
|
final_type[0]);
|
|
|
/* Tag symbols that have a R_MIPS16_26 relocation against them. */
|
/* Tag symbols that have a R_MIPS16_26 relocation against them. */
|
if (reloc_type[0] == BFD_RELOC_MIPS16_JMP
|
if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
|
&& ip->fixp[0]->fx_addsy)
|
|
*symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
|
*symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
|
|
|
/* These relocations can have an addend that won't fit in
|
/* These relocations can have an addend that won't fit in
|
4 octets for 64bit assembly. */
|
4 octets for 64bit assembly. */
|
if (HAVE_64BIT_GPRS
|
if (HAVE_64BIT_GPRS
|
Line 3365... |
Line 4417... |
for (i = 1; i < 3; i++)
|
for (i = 1; i < 3; i++)
|
if (reloc_type[i] != BFD_RELOC_UNUSED)
|
if (reloc_type[i] != BFD_RELOC_UNUSED)
|
{
|
{
|
ip->fixp[i] = fix_new (ip->frag, ip->where,
|
ip->fixp[i] = fix_new (ip->frag, ip->where,
|
ip->fixp[0]->fx_size, NULL, 0,
|
ip->fixp[0]->fx_size, NULL, 0,
|
FALSE, reloc_type[i]);
|
FALSE, final_type[i]);
|
|
|
/* Use fx_tcbit to mark compound relocs. */
|
/* Use fx_tcbit to mark compound relocs. */
|
ip->fixp[0]->fx_tcbit = 1;
|
ip->fixp[0]->fx_tcbit = 1;
|
ip->fixp[i]->fx_tcbit = 1;
|
ip->fixp[i]->fx_tcbit = 1;
|
}
|
}
|
}
|
}
|
}
|
|
install_insn (ip);
|
install_insn (ip);
|
|
|
/* Update the register mask information. */
|
/* Update the register mask information. */
|
if (! mips_opts.mips16)
|
mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
|
|
mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
|
|
|
|
switch (method)
|
{
|
{
|
if ((pinfo & INSN_WRITE_GPR_D) || (pinfo2 & INSN2_READ_GPR_D))
|
case APPEND_ADD:
|
mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
|
insert_into_history (0, 1, ip);
|
if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
|
break;
|
mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
|
|
if (pinfo & INSN_READ_GPR_S)
|
case APPEND_ADD_WITH_NOP:
|
mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip);
|
|
if (pinfo & INSN_WRITE_GPR_31)
|
|
mips_gprmask |= 1 << RA;
|
|
if (pinfo2 & (INSN2_WRITE_GPR_Z | INSN2_READ_GPR_Z))
|
|
mips_gprmask |= 1 << EXTRACT_OPERAND (RZ, *ip);
|
|
if (pinfo & INSN_WRITE_FPR_D)
|
|
mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip);
|
|
if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
|
|
mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip);
|
|
if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
|
|
mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip);
|
|
if ((pinfo & INSN_READ_FPR_R) != 0)
|
|
mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip);
|
|
if (pinfo2 & (INSN2_WRITE_FPR_Z | INSN2_READ_FPR_Z))
|
|
mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FZ, *ip);
|
|
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))
|
struct mips_cl_insn *nop;
|
mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip);
|
|
if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
|
insert_into_history (0, 1, ip);
|
mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip);
|
nop = get_delay_slot_nop (ip);
|
if (pinfo & MIPS16_INSN_WRITE_Z)
|
add_fixed_insn (nop);
|
mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip);
|
insert_into_history (0, 1, nop);
|
if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
|
if (mips_relax.sequence)
|
mips_gprmask |= 1 << TREG;
|
mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
|
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 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip);
|
|
if (pinfo & MIPS16_INSN_READ_GPR_X)
|
|
mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
|
|
}
|
}
|
|
break;
|
|
|
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
|
case APPEND_ADD_COMPACT:
|
{
|
|
/* 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
|
|
/* We can't swap if the previous instruction's position
|
|
is fixed. */
|
|
|| history[0].fixed_p
|
|
/* 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. */
|
|
|| history[1].noreorder_p
|
|
/* 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. */
|
|
|| si->label_list != NULL
|
|
/* If the previous instruction is in a variant frag
|
|
other than this branch's one, we cannot do the swap.
|
|
This does not apply to the mips16, which uses variant
|
|
frags for different purposes. */
|
|
|| (! mips_opts.mips16
|
|
&& prev_insn_frag_type == rs_machine_dependent)
|
|
/* Check for conflicts between the branch and the instructions
|
|
before the candidate delay slot. */
|
|
|| nops_for_insn (history + 1, ip) > 0
|
|
/* Check for conflicts between the swapped sequence and the
|
|
target of the branch. */
|
|
|| nops_for_sequence (2, history + 1, ip, history) > 0
|
|
/* 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, EXTRACT_OPERAND (RT, history[0]),
|
|
MIPS_GR_REG))
|
|
|| (! mips_opts.mips16
|
|
&& (prev_pinfo & INSN_WRITE_GPR_D)
|
|
&& insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]),
|
|
MIPS_GR_REG))
|
|
|| (! mips_opts.mips16
|
|
&& (prev_pinfo2 & INSN2_WRITE_GPR_Z)
|
|
&& insn_uses_reg (ip, EXTRACT_OPERAND (RZ, history[0]),
|
|
MIPS_GR_REG))
|
|
|| (mips_opts.mips16
|
|
&& (((prev_pinfo & MIPS16_INSN_WRITE_X)
|
|
&& (insn_uses_reg
|
|
(ip, MIPS16_EXTRACT_OPERAND (RX, history[0]),
|
|
MIPS16_REG)))
|
|
|| ((prev_pinfo & MIPS16_INSN_WRITE_Y)
|
|
&& (insn_uses_reg
|
|
(ip, MIPS16_EXTRACT_OPERAND (RY, history[0]),
|
|
MIPS16_REG)))
|
|
|| ((prev_pinfo & MIPS16_INSN_WRITE_Z)
|
|
&& (insn_uses_reg
|
|
(ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]),
|
|
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
|
|
(history[0].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)
|
|
&& (EXTRACT_OPERAND (RT, history[0])
|
|
== EXTRACT_OPERAND (RD, *ip)))
|
|
|| ((pinfo & INSN_WRITE_GPR_31)
|
|
&& EXTRACT_OPERAND (RT, history[0]) == RA)))
|
|
|| (! mips_opts.mips16
|
|
&& (prev_pinfo & INSN_WRITE_GPR_D)
|
|
&& (((pinfo & INSN_WRITE_GPR_D)
|
|
&& (EXTRACT_OPERAND (RD, history[0])
|
|
== EXTRACT_OPERAND (RD, *ip)))
|
|
|| ((pinfo & INSN_WRITE_GPR_31)
|
|
&& EXTRACT_OPERAND (RD, history[0]) == RA)))
|
|
|| (mips_opts.mips16
|
|
&& (pinfo & MIPS16_INSN_WRITE_31)
|
|
&& ((prev_pinfo & MIPS16_INSN_WRITE_31)
|
|
|| ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
|
|
&& (MIPS16OP_EXTRACT_REG32R (history[0].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 (&history[0],
|
|
EXTRACT_OPERAND (RD, *ip),
|
|
MIPS_GR_REG))
|
|
|| (! mips_opts.mips16
|
|
&& (pinfo & INSN_WRITE_GPR_31)
|
|
&& insn_uses_reg (&history[0], RA, MIPS_GR_REG))
|
|
|| (mips_opts.mips16
|
|
&& (pinfo & MIPS16_INSN_WRITE_31)
|
|
&& insn_uses_reg (&history[0], RA, 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 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 && history[0].fixp[0])
|
|
/* If the previous instruction is a sync, sync.l, or
|
|
sync.p, we can not swap. */
|
|
|| (prev_pinfo & INSN_SYNC)
|
|
/* If the previous instruction is an ERET or
|
|
DERET, avoid the swap. */
|
|
|| (history[0].insn_opcode == INSN_ERET)
|
|
|| (history[0].insn_opcode == INSN_DERET))
|
|
{
|
|
if (mips_opts.mips16
|
|
&& (pinfo & INSN_UNCOND_BRANCH_DELAY)
|
|
&& (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31))
|
|
&& ISA_SUPPORTS_MIPS16E)
|
|
{
|
|
/* Convert MIPS16 jr/jalr into a "compact" jump. */
|
/* Convert MIPS16 jr/jalr into a "compact" jump. */
|
|
gas_assert (mips_opts.mips16);
|
ip->insn_opcode |= 0x0080;
|
ip->insn_opcode |= 0x0080;
|
|
find_altered_mips16_opcode (ip);
|
install_insn (ip);
|
install_insn (ip);
|
insert_into_history (0, 1, ip);
|
insert_into_history (0, 1, ip);
|
}
|
break;
|
else
|
|
{
|
|
/* 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. */
|
|
insert_into_history (0, 1, ip);
|
|
emit_nop ();
|
|
}
|
|
|
|
if (mips_relax.sequence)
|
case APPEND_SWAP:
|
mips_relax.sizes[mips_relax.sequence - 1] += 4;
|
|
}
|
|
else
|
|
{
|
{
|
/* It looks like we can actually do the swap. */
|
|
struct mips_cl_insn delay = history[0];
|
struct mips_cl_insn delay = history[0];
|
if (mips_opts.mips16)
|
if (mips_opts.mips16)
|
{
|
{
|
know (delay.frag == ip->frag);
|
know (delay.frag == ip->frag);
|
move_insn (ip, delay.frag, delay.where);
|
move_insn (ip, delay.frag, delay.where);
|
Line 3616... |
Line 4473... |
{
|
{
|
/* Add the delay slot instruction to the end of the
|
/* Add the delay slot instruction to the end of the
|
current frag and shrink the fixed part of the
|
current frag and shrink the fixed part of the
|
original frag. If the branch occupies the tail of
|
original frag. If the branch occupies the tail of
|
the latter, move it backwards to cover the gap. */
|
the latter, move it backwards to cover the gap. */
|
delay.frag->fr_fix -= 4;
|
delay.frag->fr_fix -= branch_disp;
|
if (delay.frag == ip->frag)
|
if (delay.frag == ip->frag)
|
move_insn (ip, ip->frag, ip->where - 4);
|
move_insn (ip, ip->frag, ip->where - branch_disp);
|
add_fixed_insn (&delay);
|
add_fixed_insn (&delay);
|
}
|
}
|
else
|
else
|
{
|
{
|
move_insn (&delay, ip->frag, ip->where);
|
move_insn (&delay, ip->frag,
|
|
ip->where - branch_disp + insn_length (ip));
|
move_insn (ip, history[0].frag, history[0].where);
|
move_insn (ip, history[0].frag, history[0].where);
|
}
|
}
|
history[0] = *ip;
|
history[0] = *ip;
|
delay.fixed_p = 1;
|
delay.fixed_p = 1;
|
insert_into_history (0, 1, &delay);
|
insert_into_history (0, 1, &delay);
|
}
|
}
|
|
break;
|
|
}
|
|
|
/* If that was an unconditional branch, forget the previous
|
/* If we have just completed an unconditional branch, clear the history. */
|
insn information. */
|
if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
|
if (pinfo & INSN_UNCOND_BRANCH_DELAY)
|
|| (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
|
{
|
|
mips_no_prev_insn ();
|
mips_no_prev_insn ();
|
}
|
|
}
|
/* We need to emit a label at the end of branch-likely macros. */
|
else if (pinfo & INSN_COND_BRANCH_LIKELY)
|
if (emit_branch_likely_macro)
|
{
|
{
|
/* We don't yet optimize a branch likely. What we should do
|
emit_branch_likely_macro = FALSE;
|
is look at the target, copy the instruction found there
|
micromips_add_label ();
|
into the delay slot, and increment the branch to jump to
|
|
the next instruction. */
|
|
insert_into_history (0, 1, ip);
|
|
emit_nop ();
|
|
}
|
|
else
|
|
insert_into_history (0, 1, ip);
|
|
}
|
}
|
else
|
|
insert_into_history (0, 1, ip);
|
|
|
|
/* We just output an insn, so the next one doesn't have a label. */
|
/* We just output an insn, so the next one doesn't have a label. */
|
mips_clear_insn_labels ();
|
mips_clear_insn_labels ();
|
}
|
}
|
|
|
Line 3676... |
Line 4526... |
void
|
void
|
mips_emit_delays (void)
|
mips_emit_delays (void)
|
{
|
{
|
if (! mips_opts.noreorder)
|
if (! mips_opts.noreorder)
|
{
|
{
|
int nops = nops_for_insn (history, NULL);
|
int nops = nops_for_insn (0, history, NULL);
|
if (nops > 0)
|
if (nops > 0)
|
{
|
{
|
while (nops-- > 0)
|
while (nops-- > 0)
|
add_fixed_insn (NOP_INSN);
|
add_fixed_insn (NOP_INSN);
|
mips_move_labels ();
|
mips_move_labels ();
|
Line 3704... |
Line 4554... |
history[i].fixed_p = 1;
|
history[i].fixed_p = 1;
|
|
|
/* Insert any nops that might be needed between the .set noreorder
|
/* Insert any nops that might be needed between the .set noreorder
|
block and the previous instructions. We will later remove any
|
block and the previous instructions. We will later remove any
|
nops that turn out not to be needed. */
|
nops that turn out not to be needed. */
|
nops = nops_for_insn (history, NULL);
|
nops = nops_for_insn (0, history, NULL);
|
if (nops > 0)
|
if (nops > 0)
|
{
|
{
|
if (mips_optimize != 0)
|
if (mips_optimize != 0)
|
{
|
{
|
/* Record the frag which holds the nop instructions, so
|
/* Record the frag which holds the nop instructions, so
|
that we can remove them if we don't need them. */
|
that we can remove them if we don't need them. */
|
frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4);
|
frag_grow (nops * NOP_INSN_SIZE);
|
prev_nop_frag = frag_now;
|
prev_nop_frag = frag_now;
|
prev_nop_frag_holds = nops;
|
prev_nop_frag_holds = nops;
|
prev_nop_frag_required = 0;
|
prev_nop_frag_required = 0;
|
prev_nop_frag_since = 0;
|
prev_nop_frag_since = 0;
|
}
|
}
|
Line 3727... |
Line 4577... |
decrease the size of prev_nop_frag. */
|
decrease the size of prev_nop_frag. */
|
frag_wane (frag_now);
|
frag_wane (frag_now);
|
frag_new (0);
|
frag_new (0);
|
mips_move_labels ();
|
mips_move_labels ();
|
}
|
}
|
mips16_mark_labels ();
|
mips_mark_labels ();
|
mips_clear_insn_labels ();
|
mips_clear_insn_labels ();
|
}
|
}
|
mips_opts.noreorder++;
|
mips_opts.noreorder++;
|
mips_any_noreorder = 1;
|
mips_any_noreorder = 1;
|
}
|
}
|
Line 3746... |
Line 4596... |
if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
|
if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
|
{
|
{
|
/* Commit to inserting prev_nop_frag_required nops and go back to
|
/* Commit to inserting prev_nop_frag_required nops and go back to
|
handling nop insertion the .set reorder way. */
|
handling nop insertion the .set reorder way. */
|
prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
|
prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
|
* (mips_opts.mips16 ? 2 : 4));
|
* NOP_INSN_SIZE);
|
insert_into_history (prev_nop_frag_since,
|
insert_into_history (prev_nop_frag_since,
|
prev_nop_frag_required, NOP_INSN);
|
prev_nop_frag_required, NOP_INSN);
|
prev_nop_frag = NULL;
|
prev_nop_frag = NULL;
|
}
|
}
|
}
|
}
|
Line 3759... |
Line 4609... |
|
|
static void
|
static void
|
macro_start (void)
|
macro_start (void)
|
{
|
{
|
memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
|
memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
|
|
memset (&mips_macro_warning.first_insn_sizes, 0,
|
|
sizeof (mips_macro_warning.first_insn_sizes));
|
|
memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
|
mips_macro_warning.delay_slot_p = (mips_opts.noreorder
|
mips_macro_warning.delay_slot_p = (mips_opts.noreorder
|
&& (history[0].insn_mo->pinfo
|
&& delayed_branch_p (&history[0]));
|
& (INSN_UNCOND_BRANCH_DELAY
|
switch (history[0].insn_mo->pinfo2
|
| INSN_COND_BRANCH_DELAY
|
& (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
|
| INSN_COND_BRANCH_LIKELY)) != 0);
|
{
|
}
|
case INSN2_BRANCH_DELAY_32BIT:
|
|
mips_macro_warning.delay_slot_length = 4;
|
/* Given that a macro is longer than 4 bytes, return the appropriate warning
|
break;
|
for it. Return null if no warning is needed. SUBTYPE is a bitmask of
|
case INSN2_BRANCH_DELAY_16BIT:
|
RELAX_DELAY_SLOT and RELAX_NOMACRO. */
|
mips_macro_warning.delay_slot_length = 2;
|
|
break;
|
static const char *
|
default:
|
macro_warning (relax_substateT subtype)
|
mips_macro_warning.delay_slot_length = 0;
|
|
break;
|
|
}
|
|
mips_macro_warning.first_frag = NULL;
|
|
}
|
|
|
|
/* Given that a macro is longer than one instruction or of the wrong size,
|
|
return the appropriate warning for it. Return null if no warning is
|
|
needed. SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
|
|
RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
|
|
and RELAX_NOMACRO. */
|
|
|
|
static const char *
|
|
macro_warning (relax_substateT subtype)
|
{
|
{
|
if (subtype & RELAX_DELAY_SLOT)
|
if (subtype & RELAX_DELAY_SLOT)
|
return _("Macro instruction expanded into multiple instructions"
|
return _("Macro instruction expanded into multiple instructions"
|
" in a branch delay slot");
|
" in a branch delay slot");
|
else if (subtype & RELAX_NOMACRO)
|
else if (subtype & RELAX_NOMACRO)
|
return _("Macro instruction expanded into multiple instructions");
|
return _("Macro instruction expanded into multiple instructions");
|
|
else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
|
|
| RELAX_DELAY_SLOT_SIZE_SECOND))
|
|
return ((subtype & RELAX_DELAY_SLOT_16BIT)
|
|
? _("Macro instruction expanded into a wrong size instruction"
|
|
" in a 16-bit branch delay slot")
|
|
: _("Macro instruction expanded into a wrong size instruction"
|
|
" in a 32-bit branch delay slot"));
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Finish up a macro. Emit warnings as appropriate. */
|
/* Finish up a macro. Emit warnings as appropriate. */
|
|
|
static void
|
static void
|
macro_end (void)
|
macro_end (void)
|
{
|
{
|
if (mips_macro_warning.sizes[0] > 4 || mips_macro_warning.sizes[1] > 4)
|
/* Relaxation warning flags. */
|
{
|
relax_substateT subtype = 0;
|
relax_substateT subtype;
|
|
|
|
/* Set up the relaxation warning flags. */
|
/* Check delay slot size requirements. */
|
subtype = 0;
|
if (mips_macro_warning.delay_slot_length == 2)
|
if (mips_macro_warning.sizes[1] > mips_macro_warning.sizes[0])
|
subtype |= RELAX_DELAY_SLOT_16BIT;
|
|
if (mips_macro_warning.delay_slot_length != 0)
|
|
{
|
|
if (mips_macro_warning.delay_slot_length
|
|
!= mips_macro_warning.first_insn_sizes[0])
|
|
subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
|
|
if (mips_macro_warning.delay_slot_length
|
|
!= mips_macro_warning.first_insn_sizes[1])
|
|
subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
|
|
}
|
|
|
|
/* Check instruction count requirements. */
|
|
if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
|
|
{
|
|
if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
|
subtype |= RELAX_SECOND_LONGER;
|
subtype |= RELAX_SECOND_LONGER;
|
if (mips_opts.warn_about_macros)
|
if (mips_opts.warn_about_macros)
|
subtype |= RELAX_NOMACRO;
|
subtype |= RELAX_NOMACRO;
|
if (mips_macro_warning.delay_slot_p)
|
if (mips_macro_warning.delay_slot_p)
|
subtype |= RELAX_DELAY_SLOT;
|
subtype |= RELAX_DELAY_SLOT;
|
|
}
|
|
|
if (mips_macro_warning.sizes[0] > 4 && mips_macro_warning.sizes[1] > 4)
|
/* If both alternatives fail to fill a delay slot correctly,
|
{
|
emit the warning now. */
|
/* Either the macro has a single implementation or both
|
if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
|
implementations are longer than 4 bytes. Emit the
|
&& (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
|
warning now. */
|
{
|
const char *msg = macro_warning (subtype);
|
relax_substateT s;
|
if (msg != 0)
|
const char *msg;
|
|
|
|
s = subtype & (RELAX_DELAY_SLOT_16BIT
|
|
| RELAX_DELAY_SLOT_SIZE_FIRST
|
|
| RELAX_DELAY_SLOT_SIZE_SECOND);
|
|
msg = macro_warning (s);
|
|
if (msg != NULL)
|
as_warn ("%s", msg);
|
as_warn ("%s", msg);
|
|
subtype &= ~s;
|
}
|
}
|
else
|
|
|
/* If both implementations are longer than 1 instruction, then emit the
|
|
warning now. */
|
|
if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
|
{
|
{
|
/* One implementation might need a warning but the other
|
relax_substateT s;
|
definitely doesn't. */
|
const char *msg;
|
mips_macro_warning.first_frag->fr_subtype |= subtype;
|
|
}
|
s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
|
|
msg = macro_warning (s);
|
|
if (msg != NULL)
|
|
as_warn ("%s", msg);
|
|
subtype &= ~s;
|
}
|
}
|
|
|
|
/* If any flags still set, then one implementation might need a warning
|
|
and the other either will need one of a different kind or none at all.
|
|
Pass any remaining flags over to relaxation. */
|
|
if (mips_macro_warning.first_frag != NULL)
|
|
mips_macro_warning.first_frag->fr_subtype |= subtype;
|
}
|
}
|
|
|
|
/* Instruction operand formats used in macros that vary between
|
|
standard MIPS and microMIPS code. */
|
|
|
|
static const char * const brk_fmt[2] = { "c", "mF" };
|
|
static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
|
|
static const char * const jalr_fmt[2] = { "d,s", "t,s" };
|
|
static const char * const lui_fmt[2] = { "t,u", "s,u" };
|
|
static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
|
|
static const char * const mfhl_fmt[2] = { "d", "mj" };
|
|
static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
|
|
static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
|
|
|
|
#define BRK_FMT (brk_fmt[mips_opts.micromips])
|
|
#define COP12_FMT (cop12_fmt[mips_opts.micromips])
|
|
#define JALR_FMT (jalr_fmt[mips_opts.micromips])
|
|
#define LUI_FMT (lui_fmt[mips_opts.micromips])
|
|
#define MEM12_FMT (mem12_fmt[mips_opts.micromips])
|
|
#define MFHL_FMT (mfhl_fmt[mips_opts.micromips])
|
|
#define SHFT_FMT (shft_fmt[mips_opts.micromips])
|
|
#define TRAP_FMT (trap_fmt[mips_opts.micromips])
|
|
|
/* Read a macro's relocation codes from *ARGS and store them in *R.
|
/* Read a macro's relocation codes from *ARGS and store them in *R.
|
The first argument in *ARGS will be either the code for a single
|
The first argument in *ARGS will be either the code for a single
|
relocation or -1 followed by the three codes that make up a
|
relocation or -1 followed by the three codes that make up a
|
composite relocation. */
|
composite relocation. */
|
|
|
Line 3844... |
Line 4772... |
string, and corresponding arguments. */
|
string, and corresponding arguments. */
|
|
|
static void
|
static void
|
macro_build (expressionS *ep, const char *name, const char *fmt, ...)
|
macro_build (expressionS *ep, const char *name, const char *fmt, ...)
|
{
|
{
|
const struct mips_opcode *mo;
|
const struct mips_opcode *mo = NULL;
|
struct mips_cl_insn insn;
|
|
bfd_reloc_code_real_type r[3];
|
bfd_reloc_code_real_type r[3];
|
|
const struct mips_opcode *amo;
|
|
struct hash_control *hash;
|
|
struct mips_cl_insn insn;
|
va_list args;
|
va_list args;
|
|
|
va_start (args, fmt);
|
va_start (args, fmt);
|
|
|
if (mips_opts.mips16)
|
if (mips_opts.mips16)
|
Line 3861... |
Line 4791... |
}
|
}
|
|
|
r[0] = BFD_RELOC_UNUSED;
|
r[0] = BFD_RELOC_UNUSED;
|
r[1] = BFD_RELOC_UNUSED;
|
r[1] = BFD_RELOC_UNUSED;
|
r[2] = BFD_RELOC_UNUSED;
|
r[2] = BFD_RELOC_UNUSED;
|
mo = (struct mips_opcode *) hash_find (op_hash, name);
|
hash = mips_opts.micromips ? micromips_op_hash : op_hash;
|
gas_assert (mo);
|
amo = (struct mips_opcode *) hash_find (hash, name);
|
gas_assert (strcmp (name, mo->name) == 0);
|
gas_assert (amo);
|
|
gas_assert (strcmp (name, amo->name) == 0);
|
|
|
while (1)
|
do
|
{
|
{
|
/* Search until we get a match for NAME. It is assumed here that
|
/* Search until we get a match for NAME. It is assumed here that
|
macros will never generate MDMX, MIPS-3D, or MT instructions. */
|
macros will never generate MDMX, MIPS-3D, or MT instructions.
|
if (strcmp (fmt, mo->args) == 0
|
We try to match an instruction that fulfils the branch delay
|
&& mo->pinfo != INSN_MACRO
|
slot instruction length requirement (if any) of the previous
|
&& is_opcode_valid (mo))
|
instruction. While doing this we record the first instruction
|
|
seen that matches all the other conditions and use it anyway
|
|
if the requirement cannot be met; we will issue an appropriate
|
|
warning later on. */
|
|
if (strcmp (fmt, amo->args) == 0
|
|
&& amo->pinfo != INSN_MACRO
|
|
&& is_opcode_valid (amo)
|
|
&& is_size_valid (amo))
|
|
{
|
|
if (is_delay_slot_valid (amo))
|
|
{
|
|
mo = amo;
|
break;
|
break;
|
|
}
|
|
else if (!mo)
|
|
mo = amo;
|
|
}
|
|
|
++mo;
|
++amo;
|
gas_assert (mo->name);
|
gas_assert (amo->name);
|
gas_assert (strcmp (name, mo->name) == 0);
|
|
}
|
}
|
|
while (strcmp (name, amo->name) == 0);
|
|
|
|
gas_assert (mo);
|
create_insn (&insn, mo);
|
create_insn (&insn, mo);
|
for (;;)
|
for (;;)
|
{
|
{
|
switch (*fmt++)
|
switch (*fmt++)
|
{
|
{
|
Line 3897... |
Line 4844... |
case '+':
|
case '+':
|
switch (*fmt++)
|
switch (*fmt++)
|
{
|
{
|
case 'A':
|
case 'A':
|
case 'E':
|
case 'E':
|
INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips,
|
|
EXTLSB, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'B':
|
case 'B':
|
case 'F':
|
case 'F':
|
/* Note that in the macro case, these arguments are already
|
/* Note that in the macro case, these arguments are already
|
in MSB form. (When handling the instruction in the
|
in MSB form. (When handling the instruction in the
|
non-macro case, these arguments are sizes from which
|
non-macro case, these arguments are sizes from which
|
MSB values must be calculated.) */
|
MSB values must be calculated.) */
|
INSERT_OPERAND (INSMSB, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips,
|
|
INSMSB, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'C':
|
case 'C':
|
case 'G':
|
case 'G':
|
case 'H':
|
case 'H':
|
/* Note that in the macro case, these arguments are already
|
/* Note that in the macro case, these arguments are already
|
in MSBD form. (When handling the instruction in the
|
in MSBD form. (When handling the instruction in the
|
non-macro case, these arguments are sizes from which
|
non-macro case, these arguments are sizes from which
|
MSBD values must be calculated.) */
|
MSBD values must be calculated.) */
|
INSERT_OPERAND (EXTMSBD, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips,
|
|
EXTMSBD, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'Q':
|
case 'Q':
|
INSERT_OPERAND (SEQI, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
default:
|
default:
|
internalError ();
|
internalError ();
|
}
|
}
|
continue;
|
continue;
|
|
|
case '2':
|
case '2':
|
INSERT_OPERAND (BP, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, BP, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
|
case 'n':
|
|
gas_assert (mips_opts.micromips);
|
case 't':
|
case 't':
|
case 'w':
|
case 'w':
|
case 'E':
|
case 'E':
|
INSERT_OPERAND (RT, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips, RT, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'c':
|
case 'c':
|
INSERT_OPERAND (CODE, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, CODE, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'T':
|
|
case 'W':
|
case 'W':
|
INSERT_OPERAND (FT, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
case 'T':
|
|
INSERT_OPERAND (mips_opts.micromips, FT, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'd':
|
|
case 'G':
|
case 'G':
|
|
if (mips_opts.micromips)
|
|
INSERT_OPERAND (1, RS, insn, va_arg (args, int));
|
|
else
|
|
INSERT_OPERAND (0, RD, insn, va_arg (args, int));
|
|
continue;
|
|
|
case 'K':
|
case 'K':
|
INSERT_OPERAND (RD, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
case 'd':
|
|
INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'U':
|
case 'U':
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
int tmp = va_arg (args, int);
|
int tmp = va_arg (args, int);
|
|
|
INSERT_OPERAND (RT, insn, tmp);
|
INSERT_OPERAND (0, RT, insn, tmp);
|
INSERT_OPERAND (RD, insn, tmp);
|
INSERT_OPERAND (0, RD, insn, tmp);
|
continue;
|
|
}
|
}
|
|
continue;
|
|
|
case 'V':
|
case 'V':
|
case 'S':
|
case 'S':
|
INSERT_OPERAND (FS, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, FS, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'z':
|
case 'z':
|
continue;
|
continue;
|
|
|
case '<':
|
case '<':
|
INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips,
|
|
SHAMT, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'D':
|
case 'D':
|
INSERT_OPERAND (FD, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, FD, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'B':
|
case 'B':
|
INSERT_OPERAND (CODE20, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, CODE20, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'J':
|
case 'J':
|
INSERT_OPERAND (CODE19, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, CODE19, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'q':
|
case 'q':
|
INSERT_OPERAND (CODE2, insn, va_arg (args, int));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, CODE2, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'b':
|
case 'b':
|
case 's':
|
case 's':
|
case 'r':
|
case 'r':
|
case 'v':
|
case 'v':
|
INSERT_OPERAND (RS, insn, va_arg (args, int));
|
INSERT_OPERAND (mips_opts.micromips, RS, insn, va_arg (args, int));
|
continue;
|
continue;
|
|
|
case 'i':
|
case 'i':
|
case 'j':
|
case 'j':
|
macro_read_relocs (&args, r);
|
macro_read_relocs (&args, r);
|
Line 4036... |
Line 5006... |
* We don't allow branch relaxation for these branches, as
|
* We don't allow branch relaxation for these branches, as
|
* they should only appear in ".set nomacro" anyway.
|
* they should only appear in ".set nomacro" anyway.
|
*/
|
*/
|
if (ep->X_op == O_constant)
|
if (ep->X_op == O_constant)
|
{
|
{
|
|
/* For microMIPS we always use relocations for branches.
|
|
So we should not resolve immediate values. */
|
|
gas_assert (!mips_opts.micromips);
|
|
|
if ((ep->X_add_number & 3) != 0)
|
if ((ep->X_add_number & 3) != 0)
|
as_bad (_("branch to misaligned address (0x%lx)"),
|
as_bad (_("branch to misaligned address (0x%lx)"),
|
(unsigned long) ep->X_add_number);
|
(unsigned long) ep->X_add_number);
|
if ((ep->X_add_number + 0x20000) & ~0x3ffff)
|
if ((ep->X_add_number + 0x20000) & ~0x3ffff)
|
as_bad (_("branch address range overflow (0x%lx)"),
|
as_bad (_("branch address range overflow (0x%lx)"),
|
Line 4055... |
Line 5029... |
gas_assert (ep != NULL);
|
gas_assert (ep != NULL);
|
*r = BFD_RELOC_MIPS_JMP;
|
*r = BFD_RELOC_MIPS_JMP;
|
continue;
|
continue;
|
|
|
case 'C':
|
case 'C':
|
INSERT_OPERAND (COPZ, insn, va_arg (args, unsigned long));
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
|
continue;
|
continue;
|
|
|
case 'k':
|
case 'k':
|
INSERT_OPERAND (CACHE, insn, va_arg (args, unsigned long));
|
INSERT_OPERAND (mips_opts.micromips,
|
|
CACHE, insn, va_arg (args, unsigned long));
|
|
continue;
|
|
|
|
case '|':
|
|
gas_assert (mips_opts.micromips);
|
|
INSERT_OPERAND (1, TRAP, insn, va_arg (args, int));
|
|
continue;
|
|
|
|
case '.':
|
|
gas_assert (mips_opts.micromips);
|
|
INSERT_OPERAND (1, OFFSET10, insn, va_arg (args, int));
|
|
continue;
|
|
|
|
case '\\':
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
3BITPOS, insn, va_arg (args, unsigned int));
|
|
continue;
|
|
|
|
case '~':
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
OFFSET12, insn, va_arg (args, unsigned long));
|
|
continue;
|
|
|
|
case 'N':
|
|
gas_assert (mips_opts.micromips);
|
|
INSERT_OPERAND (1, BCC, insn, va_arg (args, int));
|
|
continue;
|
|
|
|
case 'm': /* Opcode extension character. */
|
|
gas_assert (mips_opts.micromips);
|
|
switch (*fmt++)
|
|
{
|
|
case 'j':
|
|
INSERT_OPERAND (1, MJ, insn, va_arg (args, int));
|
|
break;
|
|
|
|
case 'p':
|
|
INSERT_OPERAND (1, MP, insn, va_arg (args, int));
|
|
break;
|
|
|
|
case 'F':
|
|
INSERT_OPERAND (1, IMMF, insn, va_arg (args, int));
|
|
break;
|
|
|
|
default:
|
|
internalError ();
|
|
}
|
continue;
|
continue;
|
|
|
default:
|
default:
|
internalError ();
|
internalError ();
|
}
|
}
|
break;
|
break;
|
}
|
}
|
va_end (args);
|
va_end (args);
|
gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
|
gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
|
|
|
append_insn (&insn, ep, r);
|
append_insn (&insn, ep, r, TRUE);
|
}
|
}
|
|
|
static void
|
static void
|
mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
|
mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
|
va_list *args)
|
va_list *args)
|
Line 4189... |
Line 5211... |
break;
|
break;
|
}
|
}
|
|
|
gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
|
gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
|
|
|
append_insn (&insn, ep, r);
|
append_insn (&insn, ep, r, TRUE);
|
}
|
}
|
|
|
/*
|
/*
|
* Sign-extend 32-bit mode constants that have bit 31 set and all
|
* Sign-extend 32-bit mode constants that have bit 31 set and all
|
* higher bits unset.
|
* higher bits unset.
|
Line 4224... |
Line 5246... |
/*
|
/*
|
* Generate a "jalr" instruction with a relocation hint to the called
|
* Generate a "jalr" instruction with a relocation hint to the called
|
* function. This occurs in NewABI PIC code.
|
* function. This occurs in NewABI PIC code.
|
*/
|
*/
|
static void
|
static void
|
macro_build_jalr (expressionS *ep)
|
macro_build_jalr (expressionS *ep, int cprestore)
|
{
|
{
|
|
static const bfd_reloc_code_real_type jalr_relocs[2]
|
|
= { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
|
|
bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
|
|
const char *jalr;
|
char *f = NULL;
|
char *f = NULL;
|
|
|
if (MIPS_JALR_HINT_P (ep))
|
if (MIPS_JALR_HINT_P (ep))
|
{
|
{
|
frag_grow (8);
|
frag_grow (8);
|
f = frag_more (0);
|
f = frag_more (0);
|
}
|
}
|
|
if (!mips_opts.micromips)
|
macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
|
macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
|
|
else
|
|
{
|
|
jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs";
|
|
if (MIPS_JALR_HINT_P (ep))
|
|
macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
|
|
else
|
|
macro_build (NULL, jalr, "mj", PIC_CALL_REG);
|
|
}
|
if (MIPS_JALR_HINT_P (ep))
|
if (MIPS_JALR_HINT_P (ep))
|
fix_new_exp (frag_now, f - frag_now->fr_literal,
|
fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
|
4, ep, FALSE, BFD_RELOC_MIPS_JALR);
|
|
}
|
}
|
|
|
/*
|
/*
|
* Generate a "lui" instruction.
|
* Generate a "lui" instruction.
|
*/
|
*/
|
static void
|
static void
|
macro_build_lui (expressionS *ep, int regnum)
|
macro_build_lui (expressionS *ep, int regnum)
|
{
|
{
|
expressionS high_expr;
|
|
const struct mips_opcode *mo;
|
|
struct mips_cl_insn insn;
|
|
bfd_reloc_code_real_type r[3]
|
|
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
|
|
const char *name = "lui";
|
|
const char *fmt = "t,u";
|
|
|
|
gas_assert (! mips_opts.mips16);
|
gas_assert (! mips_opts.mips16);
|
|
|
high_expr = *ep;
|
if (ep->X_op != O_constant)
|
|
|
if (high_expr.X_op == O_constant)
|
|
{
|
|
/* We can compute the instruction now without a relocation entry. */
|
|
high_expr.X_add_number = ((high_expr.X_add_number + 0x8000)
|
|
>> 16) & 0xffff;
|
|
*r = BFD_RELOC_UNUSED;
|
|
}
|
|
else
|
|
{
|
{
|
gas_assert (ep->X_op == O_symbol);
|
gas_assert (ep->X_op == O_symbol);
|
/* _gp_disp is a special case, used from s_cpload.
|
/* _gp_disp is a special case, used from s_cpload.
|
__gnu_local_gp is used if mips_no_shared. */
|
__gnu_local_gp is used if mips_no_shared. */
|
gas_assert (mips_pic == NO_PIC
|
gas_assert (mips_pic == NO_PIC
|
|| (! HAVE_NEWABI
|
|| (! HAVE_NEWABI
|
&& strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
|
&& strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
|
|| (! mips_in_shared
|
|| (! mips_in_shared
|
&& strcmp (S_GET_NAME (ep->X_add_symbol),
|
&& strcmp (S_GET_NAME (ep->X_add_symbol),
|
"__gnu_local_gp") == 0));
|
"__gnu_local_gp") == 0));
|
*r = BFD_RELOC_HI16_S;
|
|
}
|
}
|
|
|
mo = hash_find (op_hash, name);
|
macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
|
gas_assert (strcmp (name, mo->name) == 0);
|
|
gas_assert (strcmp (fmt, mo->args) == 0);
|
|
create_insn (&insn, mo);
|
|
|
|
insn.insn_opcode = insn.insn_mo->match;
|
|
INSERT_OPERAND (RT, insn, regnum);
|
|
if (*r == BFD_RELOC_UNUSED)
|
|
{
|
|
insn.insn_opcode |= high_expr.X_add_number;
|
|
append_insn (&insn, NULL, r);
|
|
}
|
|
else
|
|
append_insn (&insn, &high_expr, r);
|
|
}
|
}
|
|
|
/* Generate a sequence of instructions to do a load or store from a constant
|
/* Generate a sequence of instructions to do a load or store from a constant
|
offset off of a base register (breg) into/from a target register (treg),
|
offset off of a base register (breg) into/from a target register (treg),
|
using AT if necessary. */
|
using AT if necessary. */
|
Line 4478... |
Line 5481... |
return;
|
return;
|
}
|
}
|
else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
|
else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
|
{
|
{
|
/* 32 bit values require an lui. */
|
/* 32 bit values require an lui. */
|
macro_build (ep, "lui", "t,u", reg, BFD_RELOC_HI16);
|
macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
|
if ((ep->X_add_number & 0xffff) != 0)
|
if ((ep->X_add_number & 0xffff) != 0)
|
macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
|
macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
Line 4535... |
Line 5538... |
macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
|
macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
|
return;
|
return;
|
}
|
}
|
if (lo32.X_add_number & 0x80000000)
|
if (lo32.X_add_number & 0x80000000)
|
{
|
{
|
macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
|
macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
|
if (lo32.X_add_number & 0xffff)
|
if (lo32.X_add_number & 0xffff)
|
macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
|
macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
Line 4572... |
Line 5575... |
tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
|
tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
|
| (lo32.X_add_number >> shift));
|
| (lo32.X_add_number >> shift));
|
else
|
else
|
tmp.X_add_number = hi32.X_add_number >> (shift - 32);
|
tmp.X_add_number = hi32.X_add_number >> (shift - 32);
|
macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
|
macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
|
macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", "d,w,<",
|
macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
|
reg, reg, (shift >= 32) ? shift - 32 : shift);
|
reg, reg, (shift >= 32) ? shift - 32 : shift);
|
return;
|
return;
|
}
|
}
|
++shift;
|
++shift;
|
}
|
}
|
Line 4624... |
Line 5627... |
tmp.X_add_number = (offsetT) -1;
|
tmp.X_add_number = (offsetT) -1;
|
macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
|
macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
|
if (bit != 0)
|
if (bit != 0)
|
{
|
{
|
bit += shift;
|
bit += shift;
|
macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", "d,w,<",
|
macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
|
reg, reg, (bit >= 32) ? bit - 32 : bit);
|
reg, reg, (bit >= 32) ? bit - 32 : bit);
|
}
|
}
|
macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", "d,w,<",
|
macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
|
reg, reg, (shift >= 32) ? shift - 32 : shift);
|
reg, reg, (shift >= 32) ? shift - 32 : shift);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
Line 4644... |
Line 5647... |
}
|
}
|
if ((lo32.X_add_number & 0xffff0000) == 0)
|
if ((lo32.X_add_number & 0xffff0000) == 0)
|
{
|
{
|
if (freg != 0)
|
if (freg != 0)
|
{
|
{
|
macro_build (NULL, "dsll32", "d,w,<", reg, freg, 0);
|
macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
|
freg = reg;
|
freg = reg;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
expressionS mid16;
|
expressionS mid16;
|
|
|
if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
|
if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
|
{
|
{
|
macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
|
macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
|
macro_build (NULL, "dsrl32", "d,w,<", reg, reg, 0);
|
macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
|
return;
|
return;
|
}
|
}
|
|
|
if (freg != 0)
|
if (freg != 0)
|
{
|
{
|
macro_build (NULL, "dsll", "d,w,<", reg, freg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
|
freg = reg;
|
freg = reg;
|
}
|
}
|
mid16 = lo32;
|
mid16 = lo32;
|
mid16.X_add_number >>= 16;
|
mid16.X_add_number >>= 16;
|
macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
|
macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
|
macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
|
freg = reg;
|
freg = reg;
|
}
|
}
|
if ((lo32.X_add_number & 0xffff) != 0)
|
if ((lo32.X_add_number & 0xffff) != 0)
|
macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
|
macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
|
}
|
}
|
Line 4740... |
Line 5743... |
relax_switch ();
|
relax_switch ();
|
}
|
}
|
|
|
if (*used_at == 0 && mips_opts.at)
|
if (*used_at == 0 && mips_opts.at)
|
{
|
{
|
macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (ep, "lui", "t,u", AT, BFD_RELOC_HI16_S);
|
macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg,
|
macro_build (ep, "daddiu", "t,r,j", reg, reg,
|
BFD_RELOC_MIPS_HIGHER);
|
BFD_RELOC_MIPS_HIGHER);
|
macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
|
macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
|
macro_build (NULL, "dsll32", "d,w,<", reg, reg, 0);
|
macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
|
macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
|
macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
|
*used_at = 1;
|
*used_at = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg,
|
macro_build (ep, "daddiu", "t,r,j", reg, reg,
|
BFD_RELOC_MIPS_HIGHER);
|
BFD_RELOC_MIPS_HIGHER);
|
macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
|
macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
|
macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
|
}
|
}
|
|
|
if (mips_relax.sequence)
|
if (mips_relax.sequence)
|
relax_end ();
|
relax_end ();
|
Line 4866... |
Line 5869... |
if (HAVE_NEWABI)
|
if (HAVE_NEWABI)
|
{
|
{
|
ex.X_add_number = ep->X_add_number;
|
ex.X_add_number = ep->X_add_number;
|
ep->X_add_number = 0;
|
ep->X_add_number = 0;
|
relax_start (ep->X_add_symbol);
|
relax_start (ep->X_add_symbol);
|
macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
reg, reg, mips_gp_register);
|
reg, reg, mips_gp_register);
|
macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
|
reg, BFD_RELOC_MIPS_GOT_LO16, reg);
|
reg, BFD_RELOC_MIPS_GOT_LO16, reg);
|
if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
|
if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
|
Line 4893... |
Line 5896... |
else
|
else
|
{
|
{
|
ex.X_add_number = ep->X_add_number;
|
ex.X_add_number = ep->X_add_number;
|
ep->X_add_number = 0;
|
ep->X_add_number = 0;
|
relax_start (ep->X_add_symbol);
|
relax_start (ep->X_add_symbol);
|
macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
reg, reg, mips_gp_register);
|
reg, reg, mips_gp_register);
|
macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
|
reg, BFD_RELOC_MIPS_GOT_LO16, reg);
|
reg, BFD_RELOC_MIPS_GOT_LO16, reg);
|
relax_switch ();
|
relax_switch ();
|
Line 4936... |
Line 5939... |
/* Move the contents of register SOURCE into register DEST. */
|
/* Move the contents of register SOURCE into register DEST. */
|
|
|
static void
|
static void
|
move_register (int dest, int source)
|
move_register (int dest, int source)
|
{
|
{
|
|
/* Prefer to use a 16-bit microMIPS instruction unless the previous
|
|
instruction specifically requires a 32-bit one. */
|
|
if (mips_opts.micromips
|
|
&& !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
|
|
macro_build (NULL, "move", "mp,mj", dest, source );
|
|
else
|
macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
|
macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
|
dest, source, 0);
|
dest, source, 0);
|
}
|
}
|
|
|
/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
|
/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
|
Line 5015... |
Line 6024... |
relax_end ();
|
relax_end ();
|
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
|
}
|
}
|
|
|
|
/* Emit a sequence of instructions to emulate a branch likely operation.
|
|
BR is an ordinary branch corresponding to one to be emulated. BRNEG
|
|
is its complementing branch with the original condition negated.
|
|
CALL is set if the original branch specified the link operation.
|
|
EP, FMT, SREG and TREG specify the usual macro_build() parameters.
|
|
|
|
Code like this is produced in the noreorder mode:
|
|
|
|
BRNEG <args>, 1f
|
|
nop
|
|
b <sym>
|
|
delay slot (executed only if branch taken)
|
|
1:
|
|
|
|
or, if CALL is set:
|
|
|
|
BRNEG <args>, 1f
|
|
nop
|
|
bal <sym>
|
|
delay slot (executed only if branch taken)
|
|
1:
|
|
|
|
In the reorder mode the delay slot would be filled with a nop anyway,
|
|
so code produced is simply:
|
|
|
|
BR <args>, <sym>
|
|
nop
|
|
|
|
This function is used when producing code for the microMIPS ASE that
|
|
does not implement branch likely instructions in hardware. */
|
|
|
|
static void
|
|
macro_build_branch_likely (const char *br, const char *brneg,
|
|
int call, expressionS *ep, const char *fmt,
|
|
unsigned int sreg, unsigned int treg)
|
|
{
|
|
int noreorder = mips_opts.noreorder;
|
|
expressionS expr1;
|
|
|
|
gas_assert (mips_opts.micromips);
|
|
start_noreorder ();
|
|
if (noreorder)
|
|
{
|
|
micromips_label_expr (&expr1);
|
|
macro_build (&expr1, brneg, fmt, sreg, treg);
|
|
macro_build (NULL, "nop", "");
|
|
macro_build (ep, call ? "bal" : "b", "p");
|
|
|
|
/* Set to true so that append_insn adds a label. */
|
|
emit_branch_likely_macro = TRUE;
|
|
}
|
|
else
|
|
{
|
|
macro_build (ep, br, fmt, sreg, treg);
|
|
macro_build (NULL, "nop", "");
|
|
}
|
|
end_noreorder ();
|
|
}
|
|
|
|
/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
|
|
the condition code tested. EP specifies the branch target. */
|
|
|
|
static void
|
|
macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
|
|
{
|
|
const int call = 0;
|
|
const char *brneg;
|
|
const char *br;
|
|
|
|
switch (type)
|
|
{
|
|
case M_BC1FL:
|
|
br = "bc1f";
|
|
brneg = "bc1t";
|
|
break;
|
|
case M_BC1TL:
|
|
br = "bc1t";
|
|
brneg = "bc1f";
|
|
break;
|
|
case M_BC2FL:
|
|
br = "bc2f";
|
|
brneg = "bc2t";
|
|
break;
|
|
case M_BC2TL:
|
|
br = "bc2t";
|
|
brneg = "bc2f";
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
|
|
}
|
|
|
|
/* Emit a two-argument branch macro specified by TYPE, using SREG as
|
|
the register tested. EP specifies the branch target. */
|
|
|
|
static void
|
|
macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
|
|
{
|
|
const char *brneg = NULL;
|
|
const char *br;
|
|
int call = 0;
|
|
|
|
switch (type)
|
|
{
|
|
case M_BGEZ:
|
|
br = "bgez";
|
|
break;
|
|
case M_BGEZL:
|
|
br = mips_opts.micromips ? "bgez" : "bgezl";
|
|
brneg = "bltz";
|
|
break;
|
|
case M_BGEZALL:
|
|
gas_assert (mips_opts.micromips);
|
|
br = "bgezals";
|
|
brneg = "bltz";
|
|
call = 1;
|
|
break;
|
|
case M_BGTZ:
|
|
br = "bgtz";
|
|
break;
|
|
case M_BGTZL:
|
|
br = mips_opts.micromips ? "bgtz" : "bgtzl";
|
|
brneg = "blez";
|
|
break;
|
|
case M_BLEZ:
|
|
br = "blez";
|
|
break;
|
|
case M_BLEZL:
|
|
br = mips_opts.micromips ? "blez" : "blezl";
|
|
brneg = "bgtz";
|
|
break;
|
|
case M_BLTZ:
|
|
br = "bltz";
|
|
break;
|
|
case M_BLTZL:
|
|
br = mips_opts.micromips ? "bltz" : "bltzl";
|
|
brneg = "bgez";
|
|
break;
|
|
case M_BLTZALL:
|
|
gas_assert (mips_opts.micromips);
|
|
br = "bltzals";
|
|
brneg = "bgez";
|
|
call = 1;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
if (mips_opts.micromips && brneg)
|
|
macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
|
|
else
|
|
macro_build (ep, br, "s,p", sreg);
|
|
}
|
|
|
|
/* Emit a three-argument branch macro specified by TYPE, using SREG and
|
|
TREG as the registers tested. EP specifies the branch target. */
|
|
|
|
static void
|
|
macro_build_branch_rsrt (int type, expressionS *ep,
|
|
unsigned int sreg, unsigned int treg)
|
|
{
|
|
const char *brneg = NULL;
|
|
const int call = 0;
|
|
const char *br;
|
|
|
|
switch (type)
|
|
{
|
|
case M_BEQ:
|
|
case M_BEQ_I:
|
|
br = "beq";
|
|
break;
|
|
case M_BEQL:
|
|
case M_BEQL_I:
|
|
br = mips_opts.micromips ? "beq" : "beql";
|
|
brneg = "bne";
|
|
break;
|
|
case M_BNE:
|
|
case M_BNE_I:
|
|
br = "bne";
|
|
break;
|
|
case M_BNEL:
|
|
case M_BNEL_I:
|
|
br = mips_opts.micromips ? "bne" : "bnel";
|
|
brneg = "beq";
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
if (mips_opts.micromips && brneg)
|
|
macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
|
|
else
|
|
macro_build (ep, br, "s,t,p", sreg, treg);
|
|
}
|
|
|
/*
|
/*
|
* Build macros
|
* Build macros
|
* This routine implements the seemingly endless macro or synthesized
|
* This routine implements the seemingly endless macro or synthesized
|
* instructions and addressing modes in the mips assembly language. Many
|
* instructions and addressing modes in the mips assembly language. Many
|
* of these macros are simple and are similar to each other. These could
|
* of these macros are simple and are similar to each other. These could
|
Line 5040... |
Line 6243... |
{
|
{
|
unsigned int treg, sreg, dreg, breg;
|
unsigned int treg, sreg, dreg, breg;
|
unsigned int tempreg;
|
unsigned int tempreg;
|
int mask;
|
int mask;
|
int used_at = 0;
|
int used_at = 0;
|
|
expressionS label_expr;
|
expressionS expr1;
|
expressionS expr1;
|
|
expressionS *ep;
|
const char *s;
|
const char *s;
|
const char *s2;
|
const char *s2;
|
const char *fmt;
|
const char *fmt;
|
int likely = 0;
|
int likely = 0;
|
int dbl = 0;
|
|
int coproc = 0;
|
int coproc = 0;
|
int lr = 0;
|
int off12 = 0;
|
int imm = 0;
|
|
int call = 0;
|
int call = 0;
|
|
int jals = 0;
|
|
int dbl = 0;
|
|
int imm = 0;
|
|
int ust = 0;
|
|
int lp = 0;
|
|
int ab = 0;
|
int off;
|
int off;
|
offsetT maxnum;
|
offsetT maxnum;
|
bfd_reloc_code_real_type r;
|
bfd_reloc_code_real_type r;
|
int hold_mips_optimize;
|
int hold_mips_optimize;
|
|
|
gas_assert (! mips_opts.mips16);
|
gas_assert (! mips_opts.mips16);
|
|
|
treg = EXTRACT_OPERAND (RT, *ip);
|
treg = EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
|
dreg = EXTRACT_OPERAND (RD, *ip);
|
dreg = EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
|
sreg = breg = EXTRACT_OPERAND (RS, *ip);
|
sreg = breg = EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
|
mask = ip->insn_mo->mask;
|
mask = ip->insn_mo->mask;
|
|
|
|
label_expr.X_op = O_constant;
|
|
label_expr.X_op_symbol = NULL;
|
|
label_expr.X_add_symbol = NULL;
|
|
label_expr.X_add_number = 0;
|
|
|
expr1.X_op = O_constant;
|
expr1.X_op = O_constant;
|
expr1.X_op_symbol = NULL;
|
expr1.X_op_symbol = NULL;
|
expr1.X_add_symbol = NULL;
|
expr1.X_add_symbol = NULL;
|
expr1.X_add_number = 1;
|
expr1.X_add_number = 1;
|
|
|
switch (mask)
|
switch (mask)
|
{
|
{
|
case M_DABS:
|
case M_DABS:
|
dbl = 1;
|
dbl = 1;
|
case M_ABS:
|
case M_ABS:
|
/* bgez $a0,.+12
|
/* bgez $a0,1f
|
move v0,$a0
|
move v0,$a0
|
sub v0,$zero,$a0
|
sub v0,$zero,$a0
|
|
1:
|
*/
|
*/
|
|
|
start_noreorder ();
|
start_noreorder ();
|
|
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "bgez", "s,p", sreg);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "bgez", "s,p", sreg);
|
if (dreg == sreg)
|
if (dreg == sreg)
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
else
|
else
|
move_register (dreg, sreg);
|
move_register (dreg, sreg);
|
macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
|
macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
|
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
|
|
end_noreorder ();
|
end_noreorder ();
|
break;
|
break;
|
|
|
case M_ADD_I:
|
case M_ADD_I:
|
Line 5102... |
Line 6322... |
goto do_addi;
|
goto do_addi;
|
case M_DADD_I:
|
case M_DADD_I:
|
dbl = 1;
|
dbl = 1;
|
s = "daddi";
|
s = "daddi";
|
s2 = "dadd";
|
s2 = "dadd";
|
|
if (!mips_opts.micromips)
|
goto do_addi;
|
goto do_addi;
|
|
if (imm_expr.X_op == O_constant
|
|
&& imm_expr.X_add_number >= -0x200
|
|
&& imm_expr.X_add_number < 0x200)
|
|
{
|
|
macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
|
|
break;
|
|
}
|
|
goto do_addi_i;
|
case M_DADDU_I:
|
case M_DADDU_I:
|
dbl = 1;
|
dbl = 1;
|
s = "daddiu";
|
s = "daddiu";
|
s2 = "daddu";
|
s2 = "daddu";
|
do_addi:
|
do_addi:
|
Line 5115... |
Line 6344... |
&& imm_expr.X_add_number < 0x8000)
|
&& imm_expr.X_add_number < 0x8000)
|
{
|
{
|
macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
|
macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
|
break;
|
break;
|
}
|
}
|
|
do_addi_i:
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
|
macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
|
break;
|
break;
|
|
|
Line 5172... |
Line 6402... |
(int) imm_expr.X_add_number);
|
(int) imm_expr.X_add_number);
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
|
|
|
case M_BC1FL:
|
|
case M_BC1TL:
|
|
case M_BC2FL:
|
|
case M_BC2TL:
|
|
gas_assert (mips_opts.micromips);
|
|
macro_build_branch_ccl (mask, &offset_expr,
|
|
EXTRACT_OPERAND (1, BCC, *ip));
|
|
break;
|
|
|
case M_BEQ_I:
|
case M_BEQ_I:
|
s = "beq";
|
|
goto beq_i;
|
|
case M_BEQL_I:
|
case M_BEQL_I:
|
s = "beql";
|
|
likely = 1;
|
|
goto beq_i;
|
|
case M_BNE_I:
|
case M_BNE_I:
|
s = "bne";
|
|
goto beq_i;
|
|
case M_BNEL_I:
|
case M_BNEL_I:
|
s = "bnel";
|
|
likely = 1;
|
|
beq_i:
|
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
|
treg = 0;
|
|
else
|
{
|
{
|
macro_build (&offset_expr, s, "s,t,p", sreg, ZERO);
|
treg = AT;
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
|
load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
|
macro_build (&offset_expr, s, "s,t,p", sreg, AT);
|
}
|
|
/* Fall through. */
|
|
case M_BEQL:
|
|
case M_BNEL:
|
|
macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
|
break;
|
break;
|
|
|
case M_BGEL:
|
case M_BGEL:
|
likely = 1;
|
likely = 1;
|
case M_BGE:
|
case M_BGE:
|
if (treg == 0)
|
if (treg == 0)
|
|
macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, sreg);
|
|
else if (sreg == 0)
|
|
macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
|
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
{
|
|
macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
|
macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
|
break;
|
|
|
|
case M_BGEZL:
|
|
case M_BGEZALL:
|
|
case M_BGTZL:
|
|
case M_BLEZL:
|
|
case M_BLTZL:
|
|
case M_BLTZALL:
|
|
macro_build_branch_rs (mask, &offset_expr, sreg);
|
break;
|
break;
|
|
|
case M_BGTL_I:
|
case M_BGTL_I:
|
likely = 1;
|
likely = 1;
|
case M_BGT_I:
|
case M_BGT_I:
|
Line 5235... |
Line 6475... |
do_false:
|
do_false:
|
/* Result is always false. */
|
/* Result is always false. */
|
if (! likely)
|
if (! likely)
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
else
|
else
|
macro_build (&offset_expr, "bnel", "s,t,p", ZERO, ZERO);
|
macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
|
break;
|
break;
|
}
|
}
|
if (imm_expr.X_op != O_constant)
|
if (imm_expr.X_op != O_constant)
|
as_bad (_("Unsupported large constant"));
|
as_bad (_("Unsupported large constant"));
|
++imm_expr.X_add_number;
|
++imm_expr.X_add_number;
|
Line 5248... |
Line 6488... |
case M_BGEL_I:
|
case M_BGEL_I:
|
if (mask == M_BGEL_I)
|
if (mask == M_BGEL_I)
|
likely = 1;
|
likely = 1;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
{
|
{
|
macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
|
macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
|
|
&offset_expr, sreg);
|
break;
|
break;
|
}
|
}
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
{
|
{
|
macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
|
macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
|
|
&offset_expr, sreg);
|
break;
|
break;
|
}
|
}
|
maxnum = 0x7fffffff;
|
maxnum = 0x7fffffff;
|
if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
|
if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
|
{
|
{
|
Line 5277... |
Line 6519... |
macro_build (&offset_expr, "b", "p");
|
macro_build (&offset_expr, "b", "p");
|
break;
|
break;
|
}
|
}
|
used_at = 1;
|
used_at = 1;
|
set_at (sreg, 0);
|
set_at (sreg, 0);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
break;
|
break;
|
|
|
case M_BGEUL:
|
case M_BGEUL:
|
likely = 1;
|
likely = 1;
|
case M_BGEU:
|
case M_BGEU:
|
if (treg == 0)
|
if (treg == 0)
|
goto do_true;
|
goto do_true;
|
if (sreg == 0)
|
else if (sreg == 0)
|
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, ZERO, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "beql" : "beq",
|
|
"s,t,p", ZERO, treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
|
macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BGTUL_I:
|
case M_BGTUL_I:
|
likely = 1;
|
likely = 1;
|
case M_BGTU_I:
|
case M_BGTU_I:
|
Line 5314... |
Line 6558... |
case M_BGEUL_I:
|
case M_BGEUL_I:
|
if (mask == M_BGEUL_I)
|
if (mask == M_BGEUL_I)
|
likely = 1;
|
likely = 1;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
goto do_true;
|
goto do_true;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, sreg, ZERO);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bnel" : "bne",
|
|
"s,t,p", sreg, ZERO);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
set_at (sreg, 1);
|
set_at (sreg, 1);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BGTL:
|
case M_BGTL:
|
likely = 1;
|
likely = 1;
|
case M_BGT:
|
case M_BGT:
|
if (treg == 0)
|
if (treg == 0)
|
|
macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, sreg);
|
|
else if (sreg == 0)
|
|
macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
|
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
{
|
|
macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
|
macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BGTUL:
|
case M_BGTUL:
|
likely = 1;
|
likely = 1;
|
case M_BGTU:
|
case M_BGTU:
|
if (treg == 0)
|
if (treg == 0)
|
{
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
macro_build (&offset_expr, likely ? "bnel" : "bne",
|
&offset_expr, sreg, ZERO);
|
"s,t,p", sreg, ZERO);
|
else if (sreg == 0)
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
goto do_false;
|
goto do_false;
|
|
else
|
|
{
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
|
macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLEL:
|
case M_BLEL:
|
likely = 1;
|
likely = 1;
|
case M_BLE:
|
case M_BLE:
|
if (treg == 0)
|
if (treg == 0)
|
|
macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
|
|
else if (sreg == 0)
|
|
macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
|
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
{
|
|
macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
|
macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLEL_I:
|
case M_BLEL_I:
|
likely = 1;
|
likely = 1;
|
case M_BLE_I:
|
case M_BLE_I:
|
Line 5401... |
Line 6643... |
case M_BLT_I:
|
case M_BLT_I:
|
case M_BLTL_I:
|
case M_BLTL_I:
|
if (mask == M_BLTL_I)
|
if (mask == M_BLTL_I)
|
likely = 1;
|
likely = 1;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
|
macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
|
|
else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
|
macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
|
|
break;
|
|
}
|
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
|
{
|
|
macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
set_at (sreg, 0);
|
set_at (sreg, 0);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLEUL:
|
case M_BLEUL:
|
likely = 1;
|
likely = 1;
|
case M_BLEU:
|
case M_BLEU:
|
if (treg == 0)
|
if (treg == 0)
|
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, sreg, ZERO);
|
|
else if (sreg == 0)
|
|
goto do_true;
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "beql" : "beq",
|
|
"s,t,p", sreg, ZERO);
|
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
goto do_true;
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
|
macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
|
macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLEUL_I:
|
case M_BLEUL_I:
|
likely = 1;
|
likely = 1;
|
case M_BLEU_I:
|
case M_BLEU_I:
|
Line 5449... |
Line 6690... |
case M_BLTUL_I:
|
case M_BLTUL_I:
|
if (mask == M_BLTUL_I)
|
if (mask == M_BLTUL_I)
|
likely = 1;
|
likely = 1;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
goto do_false;
|
goto do_false;
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
|
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
|
|
&offset_expr, sreg, ZERO);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "beql" : "beq",
|
|
"s,t,p", sreg, ZERO);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
set_at (sreg, 1);
|
set_at (sreg, 1);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLTL:
|
case M_BLTL:
|
likely = 1;
|
likely = 1;
|
case M_BLT:
|
case M_BLT:
|
if (treg == 0)
|
if (treg == 0)
|
|
macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
|
|
else if (sreg == 0)
|
|
macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
|
|
break;
|
|
}
|
|
if (sreg == 0)
|
|
{
|
|
macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
|
macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_BLTUL:
|
case M_BLTUL:
|
likely = 1;
|
likely = 1;
|
case M_BLTU:
|
case M_BLTU:
|
if (treg == 0)
|
if (treg == 0)
|
goto do_false;
|
goto do_false;
|
if (sreg == 0)
|
else if (sreg == 0)
|
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, ZERO, treg);
|
|
else
|
{
|
{
|
macro_build (&offset_expr, likely ? "bnel" : "bne",
|
|
"s,t,p", ZERO, treg);
|
|
break;
|
|
}
|
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
|
macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
|
macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
|
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
|
|
&offset_expr, AT, ZERO);
|
|
}
|
break;
|
break;
|
|
|
case M_DEXT:
|
case M_DEXT:
|
{
|
{
|
/* Use unsigned arithmetic. */
|
/* Use unsigned arithmetic. */
|
Line 5606... |
Line 6847... |
do_div3:
|
do_div3:
|
if (treg == 0)
|
if (treg == 0)
|
{
|
{
|
as_warn (_("Divide by zero."));
|
as_warn (_("Divide by zero."));
|
if (mips_trap)
|
if (mips_trap)
|
macro_build (NULL, "teq", "s,t,q", ZERO, ZERO, 7);
|
macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
|
else
|
else
|
macro_build (NULL, "break", "c", 7);
|
macro_build (NULL, "break", BRK_FMT, 7);
|
break;
|
break;
|
}
|
}
|
|
|
start_noreorder ();
|
start_noreorder ();
|
if (mips_trap)
|
if (mips_trap)
|
{
|
{
|
macro_build (NULL, "teq", "s,t,q", treg, ZERO, 7);
|
macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
|
macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
|
macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
|
}
|
}
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "bne", "s,t,p", treg, ZERO);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
|
macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
|
macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
|
macro_build (NULL, "break", "c", 7);
|
macro_build (NULL, "break", BRK_FMT, 7);
|
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
}
|
}
|
expr1.X_add_number = -1;
|
expr1.X_add_number = -1;
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &expr1, dbl);
|
load_register (AT, &expr1, dbl);
|
expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
|
if (mips_opts.micromips)
|
macro_build (&expr1, "bne", "s,t,p", treg, AT);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
|
|
macro_build (&label_expr, "bne", "s,t,p", treg, AT);
|
if (dbl)
|
if (dbl)
|
{
|
{
|
expr1.X_add_number = 1;
|
expr1.X_add_number = 1;
|
load_register (AT, &expr1, dbl);
|
load_register (AT, &expr1, dbl);
|
macro_build (NULL, "dsll32", "d,w,<", AT, AT, 31);
|
macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
|
}
|
}
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 0x80000000;
|
expr1.X_add_number = 0x80000000;
|
macro_build (&expr1, "lui", "t,u", AT, BFD_RELOC_HI16);
|
macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
|
}
|
}
|
if (mips_trap)
|
if (mips_trap)
|
{
|
{
|
macro_build (NULL, "teq", "s,t,q", sreg, AT, 6);
|
macro_build (NULL, "teq", TRAP_FMT, sreg, AT, 6);
|
/* We want to close the noreorder block as soon as possible, so
|
/* We want to close the noreorder block as soon as possible, so
|
that later insns are available for delay slot filling. */
|
that later insns are available for delay slot filling. */
|
end_noreorder ();
|
end_noreorder ();
|
}
|
}
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "bne", "s,t,p", sreg, AT);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
|
|
/* We want to close the noreorder block as soon as possible, so
|
/* We want to close the noreorder block as soon as possible, so
|
that later insns are available for delay slot filling. */
|
that later insns are available for delay slot filling. */
|
end_noreorder ();
|
end_noreorder ();
|
|
|
macro_build (NULL, "break", "c", 6);
|
macro_build (NULL, "break", BRK_FMT, 6);
|
}
|
}
|
macro_build (NULL, s, "d", dreg);
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
|
macro_build (NULL, s, MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DIV_3I:
|
case M_DIV_3I:
|
s = "div";
|
s = "div";
|
s2 = "mflo";
|
s2 = "mflo";
|
Line 5703... |
Line 6957... |
do_divi:
|
do_divi:
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
|
{
|
{
|
as_warn (_("Divide by zero."));
|
as_warn (_("Divide by zero."));
|
if (mips_trap)
|
if (mips_trap)
|
macro_build (NULL, "teq", "s,t,q", ZERO, ZERO, 7);
|
macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
|
else
|
else
|
macro_build (NULL, "break", "c", 7);
|
macro_build (NULL, "break", BRK_FMT, 7);
|
break;
|
break;
|
}
|
}
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
|
{
|
{
|
if (strcmp (s2, "mflo") == 0)
|
if (strcmp (s2, "mflo") == 0)
|
Line 5732... |
Line 6986... |
}
|
}
|
|
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, s, "z,s,t", sreg, AT);
|
macro_build (NULL, s, "z,s,t", sreg, AT);
|
macro_build (NULL, s2, "d", dreg);
|
macro_build (NULL, s2, MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DIVU_3:
|
case M_DIVU_3:
|
s = "divu";
|
s = "divu";
|
s2 = "mflo";
|
s2 = "mflo";
|
Line 5754... |
Line 7008... |
s2 = "mfhi";
|
s2 = "mfhi";
|
do_divu3:
|
do_divu3:
|
start_noreorder ();
|
start_noreorder ();
|
if (mips_trap)
|
if (mips_trap)
|
{
|
{
|
macro_build (NULL, "teq", "s,t,q", treg, ZERO, 7);
|
macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
|
macro_build (NULL, s, "z,s,t", sreg, treg);
|
macro_build (NULL, s, "z,s,t", sreg, treg);
|
/* We want to close the noreorder block as soon as possible, so
|
/* We want to close the noreorder block as soon as possible, so
|
that later insns are available for delay slot filling. */
|
that later insns are available for delay slot filling. */
|
end_noreorder ();
|
end_noreorder ();
|
}
|
}
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "bne", "s,t,p", treg, ZERO);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
|
macro_build (NULL, s, "z,s,t", sreg, treg);
|
macro_build (NULL, s, "z,s,t", sreg, treg);
|
|
|
/* We want to close the noreorder block as soon as possible, so
|
/* We want to close the noreorder block as soon as possible, so
|
that later insns are available for delay slot filling. */
|
that later insns are available for delay slot filling. */
|
end_noreorder ();
|
end_noreorder ();
|
macro_build (NULL, "break", "c", 7);
|
macro_build (NULL, "break", BRK_FMT, 7);
|
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
}
|
}
|
macro_build (NULL, s2, "d", dreg);
|
macro_build (NULL, s2, MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DLCA_AB:
|
case M_DLCA_AB:
|
dbl = 1;
|
dbl = 1;
|
case M_LCA_AB:
|
case M_LCA_AB:
|
Line 5862... |
Line 7121... |
relax_switch ();
|
relax_switch ();
|
}
|
}
|
|
|
if (used_at == 0 && mips_opts.at)
|
if (used_at == 0 && mips_opts.at)
|
{
|
{
|
macro_build (&offset_expr, "lui", "t,u",
|
macro_build (&offset_expr, "lui", LUI_FMT,
|
tempreg, BFD_RELOC_MIPS_HIGHEST);
|
tempreg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (&offset_expr, "lui", "t,u",
|
macro_build (&offset_expr, "lui", LUI_FMT,
|
AT, BFD_RELOC_HI16_S);
|
AT, BFD_RELOC_HI16_S);
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
|
tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
AT, AT, BFD_RELOC_LO16);
|
AT, AT, BFD_RELOC_LO16);
|
macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0);
|
macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
|
macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
|
macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
|
used_at = 1;
|
used_at = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
macro_build (&offset_expr, "lui", "t,u",
|
macro_build (&offset_expr, "lui", LUI_FMT,
|
tempreg, BFD_RELOC_MIPS_HIGHEST);
|
tempreg, BFD_RELOC_MIPS_HIGHEST);
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
|
tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
|
macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
tempreg, tempreg, BFD_RELOC_HI16_S);
|
tempreg, tempreg, BFD_RELOC_HI16_S);
|
macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
macro_build (&offset_expr, "daddiu", "t,r,j",
|
tempreg, tempreg, BFD_RELOC_LO16);
|
tempreg, tempreg, BFD_RELOC_LO16);
|
}
|
}
|
|
|
if (mips_relax.sequence)
|
if (mips_relax.sequence)
|
Line 6164... |
Line 7423... |
&& (call || tempreg == PIC_CALL_REG))
|
&& (call || tempreg == PIC_CALL_REG))
|
{
|
{
|
lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
|
lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
|
lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
|
lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
|
}
|
}
|
macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type);
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
tempreg, tempreg, mips_gp_register);
|
tempreg, tempreg, mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
tempreg, lw_reloc_type, tempreg);
|
tempreg, lw_reloc_type, tempreg);
|
if (expr1.X_add_number == 0)
|
if (expr1.X_add_number == 0)
|
Line 6309... |
Line 7568... |
&& (call || tempreg == PIC_CALL_REG))
|
&& (call || tempreg == PIC_CALL_REG))
|
{
|
{
|
lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
|
lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
|
lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
|
lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
|
}
|
}
|
macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type);
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
tempreg, tempreg, mips_gp_register);
|
tempreg, tempreg, mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
tempreg, lw_reloc_type, tempreg);
|
tempreg, lw_reloc_type, tempreg);
|
|
|
Line 6374... |
Line 7633... |
if (breg != 0)
|
if (breg != 0)
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
|
break;
|
break;
|
|
|
case M_MSGSND:
|
case M_MSGSND:
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
unsigned long temp = (treg << 16) | (0x01);
|
unsigned long temp = (treg << 16) | (0x01);
|
macro_build (NULL, "c2", "C", temp);
|
macro_build (NULL, "c2", "C", temp);
|
}
|
}
|
break;
|
break;
|
|
|
case M_MSGLD:
|
case M_MSGLD:
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
unsigned long temp = (0x02);
|
unsigned long temp = (0x02);
|
macro_build (NULL, "c2", "C", temp);
|
macro_build (NULL, "c2", "C", temp);
|
}
|
}
|
break;
|
break;
|
|
|
case M_MSGLD_T:
|
case M_MSGLD_T:
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
unsigned long temp = (treg << 16) | (0x02);
|
unsigned long temp = (treg << 16) | (0x02);
|
macro_build (NULL, "c2", "C", temp);
|
macro_build (NULL, "c2", "C", temp);
|
}
|
}
|
break;
|
break;
|
|
|
case M_MSGWAIT:
|
case M_MSGWAIT:
|
|
gas_assert (!mips_opts.micromips);
|
macro_build (NULL, "c2", "C", 3);
|
macro_build (NULL, "c2", "C", 3);
|
break;
|
break;
|
|
|
case M_MSGWAIT_T:
|
case M_MSGWAIT_T:
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
unsigned long temp = (treg << 16) | 0x03;
|
unsigned long temp = (treg << 16) | 0x03;
|
macro_build (NULL, "c2", "C", temp);
|
macro_build (NULL, "c2", "C", temp);
|
}
|
}
|
break;
|
break;
|
Line 6418... |
Line 7682... |
break;
|
break;
|
|
|
/* The jal instructions must be handled as macros because when
|
/* The jal instructions must be handled as macros because when
|
generating PIC code they expand to multi-instruction
|
generating PIC code they expand to multi-instruction
|
sequences. Normally they are simple instructions. */
|
sequences. Normally they are simple instructions. */
|
|
case M_JALS_1:
|
|
dreg = RA;
|
|
/* Fall through. */
|
|
case M_JALS_2:
|
|
gas_assert (mips_opts.micromips);
|
|
jals = 1;
|
|
goto jal;
|
case M_JAL_1:
|
case M_JAL_1:
|
dreg = RA;
|
dreg = RA;
|
/* Fall through. */
|
/* Fall through. */
|
case M_JAL_2:
|
case M_JAL_2:
|
|
jal:
|
if (mips_pic == NO_PIC)
|
if (mips_pic == NO_PIC)
|
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
{
|
|
s = jals ? "jalrs" : "jalr";
|
|
if (mips_opts.micromips && dreg == RA)
|
|
macro_build (NULL, s, "mj", sreg);
|
|
else
|
|
macro_build (NULL, s, JALR_FMT, dreg, sreg);
|
|
}
|
else
|
else
|
{
|
{
|
|
int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
|
|
&& mips_cprestore_offset >= 0);
|
|
|
if (sreg != PIC_CALL_REG)
|
if (sreg != PIC_CALL_REG)
|
as_warn (_("MIPS PIC call to register other than $25"));
|
as_warn (_("MIPS PIC call to register other than $25"));
|
|
|
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore)
|
|
? "jalrs" : "jalr");
|
|
if (mips_opts.micromips && dreg == RA)
|
|
macro_build (NULL, s, "mj", sreg);
|
|
else
|
|
macro_build (NULL, s, JALR_FMT, dreg, sreg);
|
if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
|
if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
|
{
|
{
|
if (mips_cprestore_offset < 0)
|
if (mips_cprestore_offset < 0)
|
as_warn (_("No .cprestore pseudo-op used in PIC code"));
|
as_warn (_("No .cprestore pseudo-op used in PIC code"));
|
else
|
else
|
Line 6461... |
Line 7747... |
}
|
}
|
}
|
}
|
|
|
break;
|
break;
|
|
|
|
case M_JALS_A:
|
|
gas_assert (mips_opts.micromips);
|
|
jals = 1;
|
|
/* Fall through. */
|
case M_JAL_A:
|
case M_JAL_A:
|
if (mips_pic == NO_PIC)
|
if (mips_pic == NO_PIC)
|
macro_build (&offset_expr, "jal", "a");
|
macro_build (&offset_expr, jals ? "jals" : "jal", "a");
|
else if (mips_pic == SVR4_PIC)
|
else if (mips_pic == SVR4_PIC)
|
{
|
{
|
/* If this is a reference to an external symbol, and we are
|
/* If this is a reference to an external symbol, and we are
|
using a small GOT, we want
|
using a small GOT, we want
|
lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
|
lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
|
Line 6511... |
Line 7801... |
relax_end ();
|
relax_end ();
|
}
|
}
|
else
|
else
|
{
|
{
|
relax_start (offset_expr.X_add_symbol);
|
relax_start (offset_expr.X_add_symbol);
|
macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG,
|
macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
|
BFD_RELOC_MIPS_CALL_HI16);
|
BFD_RELOC_MIPS_CALL_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
|
PIC_CALL_REG, mips_gp_register);
|
PIC_CALL_REG, mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
|
PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
|
Line 6528... |
Line 7818... |
PIC_CALL_REG, PIC_CALL_REG,
|
PIC_CALL_REG, PIC_CALL_REG,
|
BFD_RELOC_MIPS_GOT_OFST);
|
BFD_RELOC_MIPS_GOT_OFST);
|
relax_end ();
|
relax_end ();
|
}
|
}
|
|
|
macro_build_jalr (&offset_expr);
|
macro_build_jalr (&offset_expr, 0);
|
}
|
}
|
else
|
else
|
{
|
{
|
relax_start (offset_expr.X_add_symbol);
|
relax_start (offset_expr.X_add_symbol);
|
if (!mips_big_got)
|
if (!mips_big_got)
|
Line 6546... |
Line 7836... |
else
|
else
|
{
|
{
|
int gpdelay;
|
int gpdelay;
|
|
|
gpdelay = reg_needs_delay (mips_gp_register);
|
gpdelay = reg_needs_delay (mips_gp_register);
|
macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG,
|
macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
|
BFD_RELOC_MIPS_CALL_HI16);
|
BFD_RELOC_MIPS_CALL_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
|
PIC_CALL_REG, mips_gp_register);
|
PIC_CALL_REG, mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
|
PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
|
Line 6565... |
Line 7855... |
mips_gp_register);
|
mips_gp_register);
|
load_delay_nop ();
|
load_delay_nop ();
|
macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
|
macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
|
PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
|
PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
|
relax_end ();
|
relax_end ();
|
macro_build_jalr (&offset_expr);
|
macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
|
|
|
if (mips_cprestore_offset < 0)
|
if (mips_cprestore_offset < 0)
|
as_warn (_("No .cprestore pseudo-op used in PIC code"));
|
as_warn (_("No .cprestore pseudo-op used in PIC code"));
|
else
|
else
|
{
|
{
|
Line 6600... |
Line 7890... |
else
|
else
|
abort ();
|
abort ();
|
|
|
break;
|
break;
|
|
|
|
case M_ACLR_AB:
|
|
ab = 1;
|
|
case M_ACLR_OB:
|
|
s = "aclr";
|
|
treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
|
|
fmt = "\\,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
case M_ASET_AB:
|
|
ab = 1;
|
|
case M_ASET_OB:
|
|
s = "aset";
|
|
treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
|
|
fmt = "\\,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
case M_LB_AB:
|
case M_LB_AB:
|
|
ab = 1;
|
s = "lb";
|
s = "lb";
|
|
fmt = "t,o(b)";
|
goto ld;
|
goto ld;
|
case M_LBU_AB:
|
case M_LBU_AB:
|
|
ab = 1;
|
s = "lbu";
|
s = "lbu";
|
|
fmt = "t,o(b)";
|
goto ld;
|
goto ld;
|
case M_LH_AB:
|
case M_LH_AB:
|
|
ab = 1;
|
s = "lh";
|
s = "lh";
|
|
fmt = "t,o(b)";
|
goto ld;
|
goto ld;
|
case M_LHU_AB:
|
case M_LHU_AB:
|
|
ab = 1;
|
s = "lhu";
|
s = "lhu";
|
|
fmt = "t,o(b)";
|
goto ld;
|
goto ld;
|
case M_LW_AB:
|
case M_LW_AB:
|
|
ab = 1;
|
s = "lw";
|
s = "lw";
|
|
fmt = "t,o(b)";
|
goto ld;
|
goto ld;
|
case M_LWC0_AB:
|
case M_LWC0_AB:
|
|
ab = 1;
|
|
gas_assert (!mips_opts.micromips);
|
s = "lwc0";
|
s = "lwc0";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LWC1_AB:
|
case M_LWC1_AB:
|
|
ab = 1;
|
s = "lwc1";
|
s = "lwc1";
|
|
fmt = "T,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LWC2_AB:
|
case M_LWC2_AB:
|
|
ab = 1;
|
|
case M_LWC2_OB:
|
s = "lwc2";
|
s = "lwc2";
|
|
fmt = COP12_FMT;
|
|
off12 = mips_opts.micromips;
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LWC3_AB:
|
case M_LWC3_AB:
|
|
ab = 1;
|
|
gas_assert (!mips_opts.micromips);
|
s = "lwc3";
|
s = "lwc3";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LWL_AB:
|
case M_LWL_AB:
|
|
ab = 1;
|
|
case M_LWL_OB:
|
s = "lwl";
|
s = "lwl";
|
lr = 1;
|
fmt = MEM12_FMT;
|
goto ld;
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_LWR_AB:
|
case M_LWR_AB:
|
|
ab = 1;
|
|
case M_LWR_OB:
|
s = "lwr";
|
s = "lwr";
|
lr = 1;
|
fmt = MEM12_FMT;
|
goto ld;
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_LDC1_AB:
|
case M_LDC1_AB:
|
|
ab = 1;
|
s = "ldc1";
|
s = "ldc1";
|
|
fmt = "T,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LDC2_AB:
|
case M_LDC2_AB:
|
|
ab = 1;
|
|
case M_LDC2_OB:
|
s = "ldc2";
|
s = "ldc2";
|
|
fmt = COP12_FMT;
|
|
off12 = mips_opts.micromips;
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LDC3_AB:
|
case M_LDC3_AB:
|
|
ab = 1;
|
s = "ldc3";
|
s = "ldc3";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto ld;
|
goto ld_st;
|
case M_LDL_AB:
|
case M_LDL_AB:
|
|
ab = 1;
|
|
case M_LDL_OB:
|
s = "ldl";
|
s = "ldl";
|
lr = 1;
|
fmt = MEM12_FMT;
|
goto ld;
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_LDR_AB:
|
case M_LDR_AB:
|
|
ab = 1;
|
|
case M_LDR_OB:
|
s = "ldr";
|
s = "ldr";
|
lr = 1;
|
fmt = MEM12_FMT;
|
goto ld;
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_LL_AB:
|
case M_LL_AB:
|
|
ab = 1;
|
|
case M_LL_OB:
|
s = "ll";
|
s = "ll";
|
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
goto ld;
|
goto ld;
|
case M_LLD_AB:
|
case M_LLD_AB:
|
|
ab = 1;
|
|
case M_LLD_OB:
|
s = "lld";
|
s = "lld";
|
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
goto ld;
|
goto ld;
|
case M_LWU_AB:
|
case M_LWU_AB:
|
|
ab = 1;
|
|
case M_LWU_OB:
|
s = "lwu";
|
s = "lwu";
|
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld;
|
|
case M_LWP_AB:
|
|
ab = 1;
|
|
case M_LWP_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "lwp";
|
|
fmt = "t,~(b)";
|
|
off12 = 1;
|
|
lp = 1;
|
|
goto ld;
|
|
case M_LDP_AB:
|
|
ab = 1;
|
|
case M_LDP_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "ldp";
|
|
fmt = "t,~(b)";
|
|
off12 = 1;
|
|
lp = 1;
|
|
goto ld;
|
|
case M_LWM_AB:
|
|
ab = 1;
|
|
case M_LWM_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "lwm";
|
|
fmt = "n,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
case M_LDM_AB:
|
|
ab = 1;
|
|
case M_LDM_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "ldm";
|
|
fmt = "n,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
|
ld:
|
ld:
|
if (breg == treg || coproc || lr)
|
if (breg == treg + lp)
|
{
|
|
tempreg = AT;
|
|
used_at = 1;
|
|
}
|
|
else
|
|
{
|
|
tempreg = treg;
|
|
}
|
|
goto ld_st;
|
goto ld_st;
|
|
else
|
|
tempreg = treg + lp;
|
|
goto ld_noat;
|
|
|
case M_SB_AB:
|
case M_SB_AB:
|
|
ab = 1;
|
s = "sb";
|
s = "sb";
|
goto st;
|
fmt = "t,o(b)";
|
|
goto ld_st;
|
case M_SH_AB:
|
case M_SH_AB:
|
|
ab = 1;
|
s = "sh";
|
s = "sh";
|
goto st;
|
fmt = "t,o(b)";
|
|
goto ld_st;
|
case M_SW_AB:
|
case M_SW_AB:
|
|
ab = 1;
|
s = "sw";
|
s = "sw";
|
goto st;
|
fmt = "t,o(b)";
|
|
goto ld_st;
|
case M_SWC0_AB:
|
case M_SWC0_AB:
|
|
ab = 1;
|
|
gas_assert (!mips_opts.micromips);
|
s = "swc0";
|
s = "swc0";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SWC1_AB:
|
case M_SWC1_AB:
|
|
ab = 1;
|
s = "swc1";
|
s = "swc1";
|
|
fmt = "T,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SWC2_AB:
|
case M_SWC2_AB:
|
|
ab = 1;
|
|
case M_SWC2_OB:
|
s = "swc2";
|
s = "swc2";
|
|
fmt = COP12_FMT;
|
|
off12 = mips_opts.micromips;
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SWC3_AB:
|
case M_SWC3_AB:
|
|
ab = 1;
|
|
gas_assert (!mips_opts.micromips);
|
s = "swc3";
|
s = "swc3";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SWL_AB:
|
case M_SWL_AB:
|
|
ab = 1;
|
|
case M_SWL_OB:
|
s = "swl";
|
s = "swl";
|
goto st;
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_SWR_AB:
|
case M_SWR_AB:
|
|
ab = 1;
|
|
case M_SWR_OB:
|
s = "swr";
|
s = "swr";
|
goto st;
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_SC_AB:
|
case M_SC_AB:
|
|
ab = 1;
|
|
case M_SC_OB:
|
s = "sc";
|
s = "sc";
|
goto st;
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_SCD_AB:
|
case M_SCD_AB:
|
|
ab = 1;
|
|
case M_SCD_OB:
|
s = "scd";
|
s = "scd";
|
goto st;
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_CACHE_AB:
|
case M_CACHE_AB:
|
|
ab = 1;
|
|
case M_CACHE_OB:
|
s = "cache";
|
s = "cache";
|
goto st;
|
fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_PREF_AB:
|
case M_PREF_AB:
|
|
ab = 1;
|
|
case M_PREF_OB:
|
s = "pref";
|
s = "pref";
|
goto st;
|
fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_SDC1_AB:
|
case M_SDC1_AB:
|
|
ab = 1;
|
s = "sdc1";
|
s = "sdc1";
|
|
fmt = "T,o(b)";
|
coproc = 1;
|
coproc = 1;
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
goto st;
|
goto ld_st;
|
case M_SDC2_AB:
|
case M_SDC2_AB:
|
|
ab = 1;
|
|
case M_SDC2_OB:
|
s = "sdc2";
|
s = "sdc2";
|
|
fmt = COP12_FMT;
|
|
off12 = mips_opts.micromips;
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SDC3_AB:
|
case M_SDC3_AB:
|
|
ab = 1;
|
|
gas_assert (!mips_opts.micromips);
|
s = "sdc3";
|
s = "sdc3";
|
|
fmt = "E,o(b)";
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
goto st;
|
goto ld_st;
|
case M_SDL_AB:
|
case M_SDL_AB:
|
|
ab = 1;
|
|
case M_SDL_OB:
|
s = "sdl";
|
s = "sdl";
|
goto st;
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
case M_SDR_AB:
|
case M_SDR_AB:
|
|
ab = 1;
|
|
case M_SDR_OB:
|
s = "sdr";
|
s = "sdr";
|
st:
|
fmt = MEM12_FMT;
|
|
off12 = mips_opts.micromips;
|
|
goto ld_st;
|
|
case M_SWP_AB:
|
|
ab = 1;
|
|
case M_SWP_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "swp";
|
|
fmt = "t,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
case M_SDP_AB:
|
|
ab = 1;
|
|
case M_SDP_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "sdp";
|
|
fmt = "t,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
case M_SWM_AB:
|
|
ab = 1;
|
|
case M_SWM_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "swm";
|
|
fmt = "n,~(b)";
|
|
off12 = 1;
|
|
goto ld_st;
|
|
case M_SDM_AB:
|
|
ab = 1;
|
|
case M_SDM_OB:
|
|
gas_assert (mips_opts.micromips);
|
|
s = "sdm";
|
|
fmt = "n,~(b)";
|
|
off12 = 1;
|
|
|
|
ld_st:
|
tempreg = AT;
|
tempreg = AT;
|
used_at = 1;
|
used_at = 1;
|
ld_st:
|
ld_noat:
|
if (coproc
|
if (coproc
|
&& NO_ISA_COP (mips_opts.arch)
|
&& NO_ISA_COP (mips_opts.arch)
|
&& (ip->insn_mo->pinfo2 & (INSN2_M_FP_S | INSN2_M_FP_D)) == 0)
|
&& (ip->insn_mo->pinfo2 & (INSN2_M_FP_S | INSN2_M_FP_D)) == 0)
|
{
|
{
|
as_bad (_("Opcode not supported on this processor: %s"),
|
as_bad (_("Opcode not supported on this processor: %s"),
|
mips_cpu_info_from_arch (mips_opts.arch)->name);
|
mips_cpu_info_from_arch (mips_opts.arch)->name);
|
break;
|
break;
|
}
|
}
|
|
|
/* Itbl support may require additional care here. */
|
|
if (mask == M_LWC1_AB
|
|
|| mask == M_SWC1_AB
|
|
|| mask == M_LDC1_AB
|
|
|| mask == M_SDC1_AB
|
|
|| mask == M_L_DAB
|
|
|| mask == M_S_DAB)
|
|
fmt = "T,o(b)";
|
|
else if (mask == M_CACHE_AB || mask == M_PREF_AB)
|
|
fmt = "k,o(b)";
|
|
else if (coproc)
|
|
fmt = "E,o(b)";
|
|
else
|
|
fmt = "t,o(b)";
|
|
|
|
if (offset_expr.X_op != O_constant
|
if (offset_expr.X_op != O_constant
|
&& offset_expr.X_op != O_symbol)
|
&& offset_expr.X_op != O_symbol)
|
{
|
{
|
as_bad (_("Expression too complex"));
|
as_bad (_("Expression too complex"));
|
offset_expr.X_op = O_constant;
|
offset_expr.X_op = O_constant;
|
Line 6800... |
Line 8269... |
|
|
/* A constant expression in PIC code can be handled just as it
|
/* A constant expression in PIC code can be handled just as it
|
is in non PIC code. */
|
is in non PIC code. */
|
if (offset_expr.X_op == O_constant)
|
if (offset_expr.X_op == O_constant)
|
{
|
{
|
|
int hipart = 0;
|
|
|
expr1.X_add_number = offset_expr.X_add_number;
|
expr1.X_add_number = offset_expr.X_add_number;
|
normalize_address_expr (&expr1);
|
normalize_address_expr (&expr1);
|
if (!IS_SEXT_16BIT_NUM (expr1.X_add_number))
|
if (!off12 && !IS_SEXT_16BIT_NUM (expr1.X_add_number))
|
{
|
{
|
expr1.X_add_number = ((expr1.X_add_number + 0x8000)
|
expr1.X_add_number = ((expr1.X_add_number + 0x8000)
|
& ~(bfd_vma) 0xffff);
|
& ~(bfd_vma) 0xffff);
|
|
hipart = 1;
|
|
}
|
|
else if (off12 && !IS_SEXT_12BIT_NUM (expr1.X_add_number))
|
|
{
|
|
expr1.X_add_number = ((expr1.X_add_number + 0x800)
|
|
& ~(bfd_vma) 0xfff);
|
|
hipart = 1;
|
|
}
|
|
if (hipart)
|
|
{
|
load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
|
load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
|
if (breg != 0)
|
if (breg != 0)
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
tempreg, tempreg, breg);
|
tempreg, tempreg, breg);
|
breg = tempreg;
|
breg = tempreg;
|
}
|
}
|
|
if (!off12)
|
macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
|
macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
|
|
else
|
|
macro_build (NULL, s, fmt,
|
|
treg, (unsigned long) offset_expr.X_add_number, breg);
|
|
}
|
|
else if (off12)
|
|
{
|
|
/* A 12-bit offset field is too narrow to be used for a low-part
|
|
relocation, so load the whole address into the auxillary
|
|
register. In the case of "A(b)" addresses, we first load
|
|
absolute address "A" into the register and then add base
|
|
register "b". In the case of "o(b)" addresses, we simply
|
|
need to add 16-bit offset "o" to base register "b", and
|
|
offset_reloc already contains the relocations associated
|
|
with "o". */
|
|
if (ab)
|
|
{
|
|
load_address (tempreg, &offset_expr, &used_at);
|
|
if (breg != 0)
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
|
tempreg, tempreg, breg);
|
|
}
|
|
else
|
|
macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
|
|
tempreg, breg, -1,
|
|
offset_reloc[0], offset_reloc[1], offset_reloc[2]);
|
|
expr1.X_add_number = 0;
|
|
macro_build (NULL, s, fmt,
|
|
treg, (unsigned long) expr1.X_add_number, tempreg);
|
}
|
}
|
else if (mips_pic == NO_PIC)
|
else if (mips_pic == NO_PIC)
|
{
|
{
|
/* If this is a reference to a GP relative symbol, and there
|
/* If this is a reference to a GP relative symbol, and there
|
is no base register, we want
|
is no base register, we want
|
Line 6894... |
Line 8404... |
relax_switch ();
|
relax_switch ();
|
}
|
}
|
|
|
if (used_at == 0 && mips_opts.at)
|
if (used_at == 0 && mips_opts.at)
|
{
|
{
|
macro_build (&offset_expr, "lui", "t,u", tempreg,
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
|
BFD_RELOC_MIPS_HIGHEST);
|
BFD_RELOC_MIPS_HIGHEST);
|
macro_build (&offset_expr, "lui", "t,u", AT,
|
macro_build (&offset_expr, "lui", LUI_FMT, AT,
|
BFD_RELOC_HI16_S);
|
BFD_RELOC_HI16_S);
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
tempreg, BFD_RELOC_MIPS_HIGHER);
|
tempreg, BFD_RELOC_MIPS_HIGHER);
|
if (breg != 0)
|
if (breg != 0)
|
macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
|
macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
|
macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0);
|
macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
|
macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
|
macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
|
macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
|
macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
|
tempreg);
|
tempreg);
|
used_at = 1;
|
used_at = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
macro_build (&offset_expr, "lui", "t,u", tempreg,
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
|
BFD_RELOC_MIPS_HIGHEST);
|
BFD_RELOC_MIPS_HIGHEST);
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
tempreg, BFD_RELOC_MIPS_HIGHER);
|
tempreg, BFD_RELOC_MIPS_HIGHER);
|
macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
|
tempreg, BFD_RELOC_HI16_S);
|
tempreg, BFD_RELOC_HI16_S);
|
macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
|
macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
|
if (breg != 0)
|
if (breg != 0)
|
macro_build (NULL, "daddu", "d,v,t",
|
macro_build (NULL, "daddu", "d,v,t",
|
tempreg, tempreg, breg);
|
tempreg, tempreg, breg);
|
macro_build (&offset_expr, s, fmt, treg,
|
macro_build (&offset_expr, s, fmt, treg,
|
BFD_RELOC_LO16, tempreg);
|
BFD_RELOC_LO16, tempreg);
|
Line 7049... |
Line 8559... |
if (expr1.X_add_number < -0x8000
|
if (expr1.X_add_number < -0x8000
|
|| expr1.X_add_number >= 0x8000)
|
|| expr1.X_add_number >= 0x8000)
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
gpdelay = reg_needs_delay (mips_gp_register);
|
gpdelay = reg_needs_delay (mips_gp_register);
|
relax_start (offset_expr.X_add_symbol);
|
relax_start (offset_expr.X_add_symbol);
|
macro_build (&offset_expr, "lui", "t,u", tempreg,
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
|
BFD_RELOC_MIPS_GOT_HI16);
|
BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
|
mips_gp_register);
|
mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
|
BFD_RELOC_MIPS_GOT_LO16, tempreg);
|
BFD_RELOC_MIPS_GOT_LO16, tempreg);
|
Line 7087... |
Line 8597... |
offset_expr.X_add_number = 0;
|
offset_expr.X_add_number = 0;
|
if (expr1.X_add_number < -0x8000
|
if (expr1.X_add_number < -0x8000
|
|| expr1.X_add_number >= 0x8000)
|
|| expr1.X_add_number >= 0x8000)
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
relax_start (offset_expr.X_add_symbol);
|
relax_start (offset_expr.X_add_symbol);
|
macro_build (&offset_expr, "lui", "t,u", tempreg,
|
macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
|
BFD_RELOC_MIPS_GOT_HI16);
|
BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
|
mips_gp_register);
|
mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
|
BFD_RELOC_MIPS_GOT_LO16, tempreg);
|
BFD_RELOC_MIPS_GOT_LO16, tempreg);
|
Line 7253... |
Line 8763... |
gas_assert (offset_expr.X_op == O_symbol
|
gas_assert (offset_expr.X_op == O_symbol
|
&& offset_expr.X_add_number == 0);
|
&& offset_expr.X_add_number == 0);
|
s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
|
s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
|
if (strcmp (s, ".lit8") == 0)
|
if (strcmp (s, ".lit8") == 0)
|
{
|
{
|
if (mips_opts.isa != ISA_MIPS1)
|
if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
|
{
|
{
|
macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
|
macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
|
BFD_RELOC_MIPS_LITERAL, mips_gp_register);
|
BFD_RELOC_MIPS_LITERAL, mips_gp_register);
|
break;
|
break;
|
}
|
}
|
Line 7276... |
Line 8786... |
{
|
{
|
/* FIXME: This won't work for a 64 bit address. */
|
/* FIXME: This won't work for a 64 bit address. */
|
macro_build_lui (&offset_expr, AT);
|
macro_build_lui (&offset_expr, AT);
|
}
|
}
|
|
|
if (mips_opts.isa != ISA_MIPS1)
|
if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
|
{
|
{
|
macro_build (&offset_expr, "ldc1", "T,o(b)",
|
macro_build (&offset_expr, "ldc1", "T,o(b)",
|
treg, BFD_RELOC_LO16, AT);
|
treg, BFD_RELOC_LO16, AT);
|
break;
|
break;
|
}
|
}
|
Line 7292... |
Line 8802... |
case M_L_DOB:
|
case M_L_DOB:
|
/* Even on a big endian machine $fn comes before $fn+1. We have
|
/* Even on a big endian machine $fn comes before $fn+1. We have
|
to adjust when loading from memory. */
|
to adjust when loading from memory. */
|
r = BFD_RELOC_LO16;
|
r = BFD_RELOC_LO16;
|
dob:
|
dob:
|
|
gas_assert (!mips_opts.micromips);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
macro_build (&offset_expr, "lwc1", "T,o(b)",
|
macro_build (&offset_expr, "lwc1", "T,o(b)",
|
target_big_endian ? treg + 1 : treg, r, breg);
|
target_big_endian ? treg + 1 : treg, r, breg);
|
/* FIXME: A possible overflow which I don't know how to deal
|
/* FIXME: A possible overflow which I don't know how to deal
|
with. */
|
with. */
|
Line 7303... |
Line 8814... |
macro_build (&offset_expr, "lwc1", "T,o(b)",
|
macro_build (&offset_expr, "lwc1", "T,o(b)",
|
target_big_endian ? treg : treg + 1, r, breg);
|
target_big_endian ? treg : treg + 1, r, breg);
|
break;
|
break;
|
|
|
case M_S_DOB:
|
case M_S_DOB:
|
|
gas_assert (!mips_opts.micromips);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
/* Even on a big endian machine $fn comes before $fn+1. We have
|
/* Even on a big endian machine $fn comes before $fn+1. We have
|
to adjust when storing to memory. */
|
to adjust when storing to memory. */
|
macro_build (&offset_expr, "swc1", "T,o(b)",
|
macro_build (&offset_expr, "swc1", "T,o(b)",
|
target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
|
target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
|
Line 7314... |
Line 8826... |
macro_build (&offset_expr, "swc1", "T,o(b)",
|
macro_build (&offset_expr, "swc1", "T,o(b)",
|
target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
|
target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
|
break;
|
break;
|
|
|
case M_L_DAB:
|
case M_L_DAB:
|
|
gas_assert (!mips_opts.micromips);
|
/*
|
/*
|
* The MIPS assembler seems to check for X_add_number not
|
* The MIPS assembler seems to check for X_add_number not
|
* being double aligned and generating:
|
* being double aligned and generating:
|
* lui at,%hi(foo+1)
|
* lui at,%hi(foo+1)
|
* addu at,at,v1
|
* addu at,at,v1
|
Line 7327... |
Line 8840... |
* But, the resulting address is the same after relocation so why
|
* But, the resulting address is the same after relocation so why
|
* generate the extra instruction?
|
* generate the extra instruction?
|
*/
|
*/
|
/* Itbl support may require additional care here. */
|
/* Itbl support may require additional care here. */
|
coproc = 1;
|
coproc = 1;
|
|
fmt = "T,o(b)";
|
if (mips_opts.isa != ISA_MIPS1)
|
if (mips_opts.isa != ISA_MIPS1)
|
{
|
{
|
s = "ldc1";
|
s = "ldc1";
|
goto ld;
|
goto ld_st;
|
}
|
}
|
|
|
s = "lwc1";
|
s = "lwc1";
|
fmt = "T,o(b)";
|
|
goto ldd_std;
|
goto ldd_std;
|
|
|
case M_S_DAB:
|
case M_S_DAB:
|
|
gas_assert (!mips_opts.micromips);
|
|
/* Itbl support may require additional care here. */
|
|
coproc = 1;
|
|
fmt = "T,o(b)";
|
if (mips_opts.isa != ISA_MIPS1)
|
if (mips_opts.isa != ISA_MIPS1)
|
{
|
{
|
s = "sdc1";
|
s = "sdc1";
|
goto st;
|
goto ld_st;
|
}
|
}
|
|
|
s = "swc1";
|
s = "swc1";
|
fmt = "T,o(b)";
|
|
/* Itbl support may require additional care here. */
|
|
coproc = 1;
|
|
goto ldd_std;
|
goto ldd_std;
|
|
|
case M_LD_AB:
|
case M_LD_AB:
|
|
fmt = "t,o(b)";
|
if (HAVE_64BIT_GPRS)
|
if (HAVE_64BIT_GPRS)
|
{
|
{
|
s = "ld";
|
s = "ld";
|
goto ld;
|
goto ld;
|
}
|
}
|
|
|
s = "lw";
|
s = "lw";
|
fmt = "t,o(b)";
|
|
goto ldd_std;
|
goto ldd_std;
|
|
|
case M_SD_AB:
|
case M_SD_AB:
|
|
fmt = "t,o(b)";
|
if (HAVE_64BIT_GPRS)
|
if (HAVE_64BIT_GPRS)
|
{
|
{
|
s = "sd";
|
s = "sd";
|
goto st;
|
goto ld_st;
|
}
|
}
|
|
|
s = "sw";
|
s = "sw";
|
fmt = "t,o(b)";
|
|
|
|
ldd_std:
|
ldd_std:
|
if (offset_expr.X_op != O_symbol
|
if (offset_expr.X_op != O_symbol
|
&& offset_expr.X_op != O_constant)
|
&& offset_expr.X_op != O_constant)
|
{
|
{
|
Line 7535... |
Line 9045... |
if (expr1.X_add_number < -0x8000
|
if (expr1.X_add_number < -0x8000
|
|| expr1.X_add_number >= 0x8000 - 4)
|
|| expr1.X_add_number >= 0x8000 - 4)
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
as_bad (_("PIC code offset overflow (max 16 signed bits)"));
|
gpdelay = reg_needs_delay (mips_gp_register);
|
gpdelay = reg_needs_delay (mips_gp_register);
|
relax_start (offset_expr.X_add_symbol);
|
relax_start (offset_expr.X_add_symbol);
|
macro_build (&offset_expr, "lui", "t,u",
|
macro_build (&offset_expr, "lui", LUI_FMT,
|
AT, BFD_RELOC_MIPS_GOT_HI16);
|
AT, BFD_RELOC_MIPS_GOT_HI16);
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
AT, AT, mips_gp_register);
|
AT, AT, mips_gp_register);
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
|
AT, BFD_RELOC_MIPS_GOT_LO16, AT);
|
AT, BFD_RELOC_MIPS_GOT_LO16, AT);
|
Line 7635... |
Line 9145... |
s = "c2";
|
s = "c2";
|
goto copz;
|
goto copz;
|
case M_COP3:
|
case M_COP3:
|
s = "c3";
|
s = "c3";
|
copz:
|
copz:
|
|
gas_assert (!mips_opts.micromips);
|
if (NO_ISA_COP (mips_opts.arch)
|
if (NO_ISA_COP (mips_opts.arch)
|
&& (ip->insn_mo->pinfo2 & INSN2_M_FP_S) == 0)
|
&& (ip->insn_mo->pinfo2 & INSN2_M_FP_S) == 0)
|
{
|
{
|
as_bad (_("opcode not supported on this processor: %s"),
|
as_bad (_("opcode not supported on this processor: %s"),
|
mips_cpu_info_from_arch (mips_opts.arch)->name);
|
mips_cpu_info_from_arch (mips_opts.arch)->name);
|
Line 7656... |
Line 9167... |
|
|
case M_DMUL:
|
case M_DMUL:
|
dbl = 1;
|
dbl = 1;
|
case M_MUL:
|
case M_MUL:
|
macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
|
macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
|
macro_build (NULL, "mflo", "d", dreg);
|
macro_build (NULL, "mflo", MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DMUL_I:
|
case M_DMUL_I:
|
dbl = 1;
|
dbl = 1;
|
case M_MUL_I:
|
case M_MUL_I:
|
Line 7668... |
Line 9179... |
not trying to be that fancy. GCC should do this for us
|
not trying to be that fancy. GCC should do this for us
|
anyway. */
|
anyway. */
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
|
macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
|
macro_build (NULL, "mflo", "d", dreg);
|
macro_build (NULL, "mflo", MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DMULO_I:
|
case M_DMULO_I:
|
dbl = 1;
|
dbl = 1;
|
case M_MULO_I:
|
case M_MULO_I:
|
Line 7686... |
Line 9197... |
start_noreorder ();
|
start_noreorder ();
|
used_at = 1;
|
used_at = 1;
|
if (imm)
|
if (imm)
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
|
macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
|
macro_build (NULL, "mflo", "d", dreg);
|
macro_build (NULL, "mflo", MFHL_FMT, dreg);
|
macro_build (NULL, dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, RA);
|
macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, dreg, dreg, RA);
|
macro_build (NULL, "mfhi", "d", AT);
|
macro_build (NULL, "mfhi", MFHL_FMT, AT);
|
if (mips_trap)
|
if (mips_trap)
|
macro_build (NULL, "tne", "s,t,q", dreg, AT, 6);
|
macro_build (NULL, "tne", TRAP_FMT, dreg, AT, 6);
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "beq", "s,t,p", dreg, AT);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "beq", "s,t,p", dreg, AT);
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "break", "c", 6);
|
macro_build (NULL, "break", BRK_FMT, 6);
|
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
}
|
}
|
end_noreorder ();
|
end_noreorder ();
|
macro_build (NULL, "mflo", "d", dreg);
|
macro_build (NULL, "mflo", MFHL_FMT, dreg);
|
break;
|
break;
|
|
|
case M_DMULOU_I:
|
case M_DMULOU_I:
|
dbl = 1;
|
dbl = 1;
|
case M_MULOU_I:
|
case M_MULOU_I:
|
Line 7718... |
Line 9234... |
used_at = 1;
|
used_at = 1;
|
if (imm)
|
if (imm)
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
|
macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
|
sreg, imm ? AT : treg);
|
sreg, imm ? AT : treg);
|
macro_build (NULL, "mfhi", "d", AT);
|
macro_build (NULL, "mfhi", MFHL_FMT, AT);
|
macro_build (NULL, "mflo", "d", dreg);
|
macro_build (NULL, "mflo", MFHL_FMT, dreg);
|
if (mips_trap)
|
if (mips_trap)
|
macro_build (NULL, "tne", "s,t,q", AT, ZERO, 6);
|
macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
|
else
|
else
|
{
|
{
|
expr1.X_add_number = 8;
|
if (mips_opts.micromips)
|
macro_build (&expr1, "beq", "s,t,p", AT, ZERO);
|
micromips_label_expr (&label_expr);
|
|
else
|
|
label_expr.X_add_number = 8;
|
|
macro_build (&label_expr, "beq", "s,t,p", AT, ZERO);
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "break", "c", 6);
|
macro_build (NULL, "break", BRK_FMT, 6);
|
|
if (mips_opts.micromips)
|
|
micromips_add_label ();
|
}
|
}
|
end_noreorder ();
|
end_noreorder ();
|
break;
|
break;
|
|
|
case M_DROL:
|
case M_DROL:
|
Line 7791... |
Line 9312... |
rot = imm_expr.X_add_number & 0x3f;
|
rot = imm_expr.X_add_number & 0x3f;
|
if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
|
if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
|
{
|
{
|
rot = (64 - rot) & 0x3f;
|
rot = (64 - rot) & 0x3f;
|
if (rot >= 32)
|
if (rot >= 32)
|
macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
|
macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
|
else
|
else
|
macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
|
macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
|
break;
|
break;
|
}
|
}
|
if (rot == 0)
|
if (rot == 0)
|
{
|
{
|
macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
|
macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
|
break;
|
break;
|
}
|
}
|
l = (rot < 0x20) ? "dsll" : "dsll32";
|
l = (rot < 0x20) ? "dsll" : "dsll32";
|
rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
|
rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
|
rot &= 0x1f;
|
rot &= 0x1f;
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, l, "d,w,<", AT, sreg, rot);
|
macro_build (NULL, l, SHFT_FMT, AT, sreg, rot);
|
macro_build (NULL, rr, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, rr, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
}
|
}
|
break;
|
break;
|
|
|
case M_ROL_I:
|
case M_ROL_I:
|
Line 7820... |
Line 9341... |
if (imm_expr.X_op != O_constant)
|
if (imm_expr.X_op != O_constant)
|
as_bad (_("Improper rotate count"));
|
as_bad (_("Improper rotate count"));
|
rot = imm_expr.X_add_number & 0x1f;
|
rot = imm_expr.X_add_number & 0x1f;
|
if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
|
if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
|
{
|
{
|
macro_build (NULL, "ror", "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
|
macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, (32 - rot) & 0x1f);
|
break;
|
break;
|
}
|
}
|
if (rot == 0)
|
if (rot == 0)
|
{
|
{
|
macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
|
macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
|
break;
|
break;
|
}
|
}
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "sll", "d,w,<", AT, sreg, rot);
|
macro_build (NULL, "sll", SHFT_FMT, AT, sreg, rot);
|
macro_build (NULL, "srl", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
}
|
}
|
break;
|
break;
|
|
|
case M_DROR:
|
case M_DROR:
|
Line 7873... |
Line 9394... |
as_bad (_("Improper rotate count"));
|
as_bad (_("Improper rotate count"));
|
rot = imm_expr.X_add_number & 0x3f;
|
rot = imm_expr.X_add_number & 0x3f;
|
if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
|
if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
|
{
|
{
|
if (rot >= 32)
|
if (rot >= 32)
|
macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
|
macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
|
else
|
else
|
macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
|
macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
|
break;
|
break;
|
}
|
}
|
if (rot == 0)
|
if (rot == 0)
|
{
|
{
|
macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
|
macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
|
break;
|
break;
|
}
|
}
|
rr = (rot < 0x20) ? "dsrl" : "dsrl32";
|
rr = (rot < 0x20) ? "dsrl" : "dsrl32";
|
l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
|
l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
|
rot &= 0x1f;
|
rot &= 0x1f;
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, rr, "d,w,<", AT, sreg, rot);
|
macro_build (NULL, rr, SHFT_FMT, AT, sreg, rot);
|
macro_build (NULL, l, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, l, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
}
|
}
|
break;
|
break;
|
|
|
case M_ROR_I:
|
case M_ROR_I:
|
Line 7902... |
Line 9423... |
if (imm_expr.X_op != O_constant)
|
if (imm_expr.X_op != O_constant)
|
as_bad (_("Improper rotate count"));
|
as_bad (_("Improper rotate count"));
|
rot = imm_expr.X_add_number & 0x1f;
|
rot = imm_expr.X_add_number & 0x1f;
|
if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
|
if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
|
{
|
{
|
macro_build (NULL, "ror", "d,w,<", dreg, sreg, rot);
|
macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
|
break;
|
break;
|
}
|
}
|
if (rot == 0)
|
if (rot == 0)
|
{
|
{
|
macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
|
macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
|
break;
|
break;
|
}
|
}
|
used_at = 1;
|
used_at = 1;
|
macro_build (NULL, "srl", "d,w,<", AT, sreg, rot);
|
macro_build (NULL, "srl", SHFT_FMT, AT, sreg, rot);
|
macro_build (NULL, "sll", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "sll", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
|
}
|
}
|
break;
|
break;
|
|
|
case M_SEQ:
|
case M_SEQ:
|
Line 8142... |
Line 9663... |
used_at = 1;
|
used_at = 1;
|
}
|
}
|
macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
|
macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
|
break;
|
break;
|
|
|
|
case M_SUB_I:
|
|
s = "addi";
|
|
s2 = "sub";
|
|
goto do_subi;
|
|
case M_SUBU_I:
|
|
s = "addiu";
|
|
s2 = "subu";
|
|
goto do_subi;
|
case M_DSUB_I:
|
case M_DSUB_I:
|
dbl = 1;
|
dbl = 1;
|
case M_SUB_I:
|
s = "daddi";
|
|
s2 = "dsub";
|
|
if (!mips_opts.micromips)
|
|
goto do_subi;
|
if (imm_expr.X_op == O_constant
|
if (imm_expr.X_op == O_constant
|
&& imm_expr.X_add_number > -0x8000
|
&& imm_expr.X_add_number > -0x200
|
&& imm_expr.X_add_number <= 0x8000)
|
&& imm_expr.X_add_number <= 0x200)
|
{
|
{
|
imm_expr.X_add_number = -imm_expr.X_add_number;
|
macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
|
macro_build (&imm_expr, dbl ? "daddi" : "addi", "t,r,j",
|
|
dreg, sreg, BFD_RELOC_LO16);
|
|
break;
|
break;
|
}
|
}
|
used_at = 1;
|
goto do_subi_i;
|
load_register (AT, &imm_expr, dbl);
|
|
macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
|
|
break;
|
|
|
|
case M_DSUBU_I:
|
case M_DSUBU_I:
|
dbl = 1;
|
dbl = 1;
|
case M_SUBU_I:
|
s = "daddiu";
|
|
s2 = "dsubu";
|
|
do_subi:
|
if (imm_expr.X_op == O_constant
|
if (imm_expr.X_op == O_constant
|
&& imm_expr.X_add_number > -0x8000
|
&& imm_expr.X_add_number > -0x8000
|
&& imm_expr.X_add_number <= 0x8000)
|
&& imm_expr.X_add_number <= 0x8000)
|
{
|
{
|
imm_expr.X_add_number = -imm_expr.X_add_number;
|
imm_expr.X_add_number = -imm_expr.X_add_number;
|
macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "t,r,j",
|
macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
|
dreg, sreg, BFD_RELOC_LO16);
|
|
break;
|
break;
|
}
|
}
|
|
do_subi_i:
|
used_at = 1;
|
used_at = 1;
|
load_register (AT, &imm_expr, dbl);
|
load_register (AT, &imm_expr, dbl);
|
macro_build (NULL, dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
|
macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
|
break;
|
break;
|
|
|
case M_TEQ_I:
|
case M_TEQ_I:
|
s = "teq";
|
s = "teq";
|
goto trap;
|
goto trap;
|
Line 8201... |
Line 9729... |
macro_build (NULL, s, "s,t", sreg, AT);
|
macro_build (NULL, s, "s,t", sreg, AT);
|
break;
|
break;
|
|
|
case M_TRUNCWS:
|
case M_TRUNCWS:
|
case M_TRUNCWD:
|
case M_TRUNCWD:
|
|
gas_assert (!mips_opts.micromips);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
gas_assert (mips_opts.isa == ISA_MIPS1);
|
used_at = 1;
|
used_at = 1;
|
sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
|
sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
|
dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
|
dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
|
|
|
Line 8227... |
Line 9756... |
macro_build (NULL, "ctc1", "t,G", treg, RA);
|
macro_build (NULL, "ctc1", "t,G", treg, RA);
|
macro_build (NULL, "nop", "");
|
macro_build (NULL, "nop", "");
|
end_noreorder ();
|
end_noreorder ();
|
break;
|
break;
|
|
|
|
case M_ULH_A:
|
|
ab = 1;
|
case M_ULH:
|
case M_ULH:
|
s = "lb";
|
s = "lb";
|
goto ulh;
|
s2 = "lbu";
|
|
off = 1;
|
|
goto uld_st;
|
|
case M_ULHU_A:
|
|
ab = 1;
|
case M_ULHU:
|
case M_ULHU:
|
s = "lbu";
|
s = "lbu";
|
ulh:
|
s2 = "lbu";
|
used_at = 1;
|
off = 1;
|
if (offset_expr.X_add_number >= 0x7fff)
|
goto uld_st;
|
as_bad (_("Operand overflow"));
|
case M_ULW_A:
|
if (!target_big_endian)
|
ab = 1;
|
++offset_expr.X_add_number;
|
case M_ULW:
|
macro_build (&offset_expr, s, "t,o(b)", AT, BFD_RELOC_LO16, breg);
|
s = "lwl";
|
if (!target_big_endian)
|
s2 = "lwr";
|
--offset_expr.X_add_number;
|
off12 = mips_opts.micromips;
|
else
|
off = 3;
|
++offset_expr.X_add_number;
|
goto uld_st;
|
macro_build (&offset_expr, "lbu", "t,o(b)", treg, BFD_RELOC_LO16, breg);
|
case M_ULD_A:
|
macro_build (NULL, "sll", "d,w,<", AT, AT, 8);
|
ab = 1;
|
macro_build (NULL, "or", "d,v,t", treg, treg, AT);
|
|
break;
|
|
|
|
case M_ULD:
|
case M_ULD:
|
s = "ldl";
|
s = "ldl";
|
s2 = "ldr";
|
s2 = "ldr";
|
|
off12 = mips_opts.micromips;
|
off = 7;
|
off = 7;
|
goto ulw;
|
goto uld_st;
|
case M_ULW:
|
case M_USH_A:
|
s = "lwl";
|
ab = 1;
|
s2 = "lwr";
|
case M_USH:
|
|
s = "sb";
|
|
s2 = "sb";
|
|
off = 1;
|
|
ust = 1;
|
|
goto uld_st;
|
|
case M_USW_A:
|
|
ab = 1;
|
|
case M_USW:
|
|
s = "swl";
|
|
s2 = "swr";
|
|
off12 = mips_opts.micromips;
|
off = 3;
|
off = 3;
|
ulw:
|
ust = 1;
|
if (offset_expr.X_add_number >= 0x8000 - off)
|
goto uld_st;
|
|
case M_USD_A:
|
|
ab = 1;
|
|
case M_USD:
|
|
s = "sdl";
|
|
s2 = "sdr";
|
|
off12 = mips_opts.micromips;
|
|
off = 7;
|
|
ust = 1;
|
|
|
|
uld_st:
|
|
if (!ab && offset_expr.X_add_number >= 0x8000 - off)
|
as_bad (_("Operand overflow"));
|
as_bad (_("Operand overflow"));
|
if (treg != breg)
|
|
|
ep = &offset_expr;
|
|
expr1.X_add_number = 0;
|
|
if (ab)
|
|
{
|
|
used_at = 1;
|
|
tempreg = AT;
|
|
load_address (tempreg, ep, &used_at);
|
|
if (breg != 0)
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
|
|
tempreg, tempreg, breg);
|
|
breg = tempreg;
|
tempreg = treg;
|
tempreg = treg;
|
else
|
ep = &expr1;
|
|
}
|
|
else if (off12
|
|
&& (offset_expr.X_op != O_constant
|
|
|| !IS_SEXT_12BIT_NUM (offset_expr.X_add_number)
|
|
|| !IS_SEXT_12BIT_NUM (offset_expr.X_add_number + off)))
|
|
{
|
|
used_at = 1;
|
|
tempreg = AT;
|
|
macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg,
|
|
-1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
|
|
breg = tempreg;
|
|
tempreg = treg;
|
|
ep = &expr1;
|
|
}
|
|
else if (!ust && treg == breg)
|
{
|
{
|
used_at = 1;
|
used_at = 1;
|
tempreg = AT;
|
tempreg = AT;
|
}
|
}
|
|
else
|
|
tempreg = treg;
|
|
|
|
if (off == 1)
|
|
goto ulh_sh;
|
|
|
if (!target_big_endian)
|
if (!target_big_endian)
|
offset_expr.X_add_number += off;
|
ep->X_add_number += off;
|
macro_build (&offset_expr, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
if (!off12)
|
|
macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
|
else
|
|
macro_build (NULL, s, "t,~(b)",
|
|
tempreg, (unsigned long) ep->X_add_number, breg);
|
|
|
if (!target_big_endian)
|
if (!target_big_endian)
|
offset_expr.X_add_number -= off;
|
ep->X_add_number -= off;
|
else
|
else
|
offset_expr.X_add_number += off;
|
ep->X_add_number += off;
|
macro_build (&offset_expr, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
if (!off12)
|
|
macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
|
else
|
|
macro_build (NULL, s2, "t,~(b)",
|
|
tempreg, (unsigned long) ep->X_add_number, breg);
|
|
|
/* If necessary, move the result in tempreg to the final destination. */
|
/* If necessary, move the result in tempreg to the final destination. */
|
if (treg == tempreg)
|
if (!ust && treg != tempreg)
|
break;
|
{
|
/* Protect second load's delay slot. */
|
/* Protect second load's delay slot. */
|
load_delay_nop ();
|
load_delay_nop ();
|
move_register (treg, tempreg);
|
move_register (treg, tempreg);
|
|
}
|
break;
|
break;
|
|
|
case M_ULD_A:
|
ulh_sh:
|
s = "ldl";
|
|
s2 = "ldr";
|
|
off = 7;
|
|
goto ulwa;
|
|
case M_ULW_A:
|
|
s = "lwl";
|
|
s2 = "lwr";
|
|
off = 3;
|
|
ulwa:
|
|
used_at = 1;
|
used_at = 1;
|
load_address (AT, &offset_expr, &used_at);
|
if (target_big_endian == ust)
|
if (breg != 0)
|
ep->X_add_number += off;
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
|
tempreg = ust || ab ? treg : AT;
|
if (!target_big_endian)
|
macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
expr1.X_add_number = off;
|
|
else
|
/* For halfword transfers we need a temporary register to shuffle
|
expr1.X_add_number = 0;
|
bytes. Unfortunately for M_USH_A we have none available before
|
macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
the next store as AT holds the base address. We deal with this
|
if (!target_big_endian)
|
case by clobbering TREG and then restoring it as with ULH. */
|
expr1.X_add_number = 0;
|
tempreg = ust == ab ? treg : AT;
|
|
if (ust)
|
|
macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
|
|
|
|
if (target_big_endian == ust)
|
|
ep->X_add_number -= off;
|
else
|
else
|
expr1.X_add_number = off;
|
ep->X_add_number += off;
|
macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
|
break;
|
|
|
|
case M_ULH_A:
|
|
case M_ULHU_A:
|
|
used_at = 1;
|
|
load_address (AT, &offset_expr, &used_at);
|
|
if (breg != 0)
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
|
|
if (target_big_endian)
|
|
expr1.X_add_number = 0;
|
|
macro_build (&expr1, mask == M_ULH_A ? "lb" : "lbu", "t,o(b)",
|
|
treg, BFD_RELOC_LO16, AT);
|
|
if (target_big_endian)
|
|
expr1.X_add_number = 1;
|
|
else
|
|
expr1.X_add_number = 0;
|
|
macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
|
|
macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
|
|
macro_build (NULL, "or", "d,v,t", treg, treg, AT);
|
|
break;
|
|
|
|
case M_USH:
|
/* For M_USH_A re-retrieve the LSB. */
|
used_at = 1;
|
if (ust && ab)
|
if (offset_expr.X_add_number >= 0x7fff)
|
{
|
as_bad (_("Operand overflow"));
|
|
if (target_big_endian)
|
|
++offset_expr.X_add_number;
|
|
macro_build (&offset_expr, "sb", "t,o(b)", treg, BFD_RELOC_LO16, breg);
|
|
macro_build (NULL, "srl", "d,w,<", AT, treg, 8);
|
|
if (target_big_endian)
|
if (target_big_endian)
|
--offset_expr.X_add_number;
|
ep->X_add_number += off;
|
else
|
|
++offset_expr.X_add_number;
|
|
macro_build (&offset_expr, "sb", "t,o(b)", AT, BFD_RELOC_LO16, breg);
|
|
break;
|
|
|
|
case M_USD:
|
|
s = "sdl";
|
|
s2 = "sdr";
|
|
off = 7;
|
|
goto usw;
|
|
case M_USW:
|
|
s = "swl";
|
|
s2 = "swr";
|
|
off = 3;
|
|
usw:
|
|
if (offset_expr.X_add_number >= 0x8000 - off)
|
|
as_bad (_("Operand overflow"));
|
|
if (!target_big_endian)
|
|
offset_expr.X_add_number += off;
|
|
macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg);
|
|
if (!target_big_endian)
|
|
offset_expr.X_add_number -= off;
|
|
else
|
|
offset_expr.X_add_number += off;
|
|
macro_build (&offset_expr, s2, "t,o(b)", treg, BFD_RELOC_LO16, breg);
|
|
break;
|
|
|
|
case M_USD_A:
|
|
s = "sdl";
|
|
s2 = "sdr";
|
|
off = 7;
|
|
goto uswa;
|
|
case M_USW_A:
|
|
s = "swl";
|
|
s2 = "swr";
|
|
off = 3;
|
|
uswa:
|
|
used_at = 1;
|
|
load_address (AT, &offset_expr, &used_at);
|
|
if (breg != 0)
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
|
|
if (!target_big_endian)
|
|
expr1.X_add_number = off;
|
|
else
|
|
expr1.X_add_number = 0;
|
|
macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
|
if (!target_big_endian)
|
|
expr1.X_add_number = 0;
|
|
else
|
|
expr1.X_add_number = off;
|
|
macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
|
break;
|
|
|
|
case M_USH_A:
|
|
used_at = 1;
|
|
load_address (AT, &offset_expr, &used_at);
|
|
if (breg != 0)
|
|
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
|
|
if (!target_big_endian)
|
|
expr1.X_add_number = 0;
|
|
macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
|
macro_build (NULL, "srl", "d,w,<", treg, treg, 8);
|
|
if (!target_big_endian)
|
|
expr1.X_add_number = 1;
|
|
else
|
|
expr1.X_add_number = 0;
|
|
macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
|
|
if (!target_big_endian)
|
|
expr1.X_add_number = 0;
|
|
else
|
else
|
expr1.X_add_number = 1;
|
ep->X_add_number -= off;
|
macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
|
macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
|
macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
|
}
|
|
/* For ULH and M_USH_A OR the LSB in. */
|
|
if (!ust || ab)
|
|
{
|
|
tempreg = !ab ? AT : treg;
|
|
macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
|
macro_build (NULL, "or", "d,v,t", treg, treg, AT);
|
macro_build (NULL, "or", "d,v,t", treg, treg, AT);
|
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
/* FIXME: Check if this is one of the itbl macros, since they
|
/* FIXME: Check if this is one of the itbl macros, since they
|
are added dynamically. */
|
are added dynamically. */
|
Line 8790... |
Line 10295... |
case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break;
|
case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break;
|
case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break;
|
case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break;
|
case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break;
|
case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break;
|
case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break;
|
case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break;
|
case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break;
|
case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break;
|
|
case '\\': USE_BITS (OP_MASK_3BITPOS, OP_SH_3BITPOS); break;
|
|
case '~': USE_BITS (OP_MASK_OFFSET12, OP_SH_OFFSET12); break;
|
case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
|
case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
|
default:
|
default:
|
as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
|
as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
|
c, opc->name, opc->args);
|
c, opc->name, opc->args);
|
return 0;
|
return 0;
|
Line 8806... |
Line 10313... |
return 0;
|
return 0;
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
/* For consistency checking, verify that the length implied matches the
|
|
major opcode and that all bits are specified either by the match/mask
|
|
part of the instruction definition, or by the operand list. */
|
|
|
|
static int
|
|
validate_micromips_insn (const struct mips_opcode *opc)
|
|
{
|
|
unsigned long match = opc->match;
|
|
unsigned long mask = opc->mask;
|
|
const char *p = opc->args;
|
|
unsigned long insn_bits;
|
|
unsigned long used_bits;
|
|
unsigned long major;
|
|
unsigned int length;
|
|
char e;
|
|
char c;
|
|
|
|
if ((mask & match) != match)
|
|
{
|
|
as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"),
|
|
opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
length = micromips_insn_length (opc);
|
|
if (length != 2 && length != 4)
|
|
{
|
|
as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
|
|
"%s %s"), length, opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
major = match >> (10 + 8 * (length - 2));
|
|
if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
|
|
|| (length == 4 && (major & 7) != 0 && (major & 4) != 4))
|
|
{
|
|
as_bad (_("Internal error: bad microMIPS opcode "
|
|
"(opcode/length mismatch): %s %s"), opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
|
|
/* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
|
|
insn_bits = 1 << 4 * length;
|
|
insn_bits <<= 4 * length;
|
|
insn_bits -= 1;
|
|
used_bits = mask;
|
|
#define USE_BITS(field) \
|
|
(used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field)
|
|
while (*p)
|
|
switch (c = *p++)
|
|
{
|
|
case ',': break;
|
|
case '(': break;
|
|
case ')': break;
|
|
case '+':
|
|
e = c;
|
|
switch (c = *p++)
|
|
{
|
|
case 'A': USE_BITS (EXTLSB); break;
|
|
case 'B': USE_BITS (INSMSB); break;
|
|
case 'C': USE_BITS (EXTMSBD); break;
|
|
case 'D': USE_BITS (RS); USE_BITS (SEL); break;
|
|
case 'E': USE_BITS (EXTLSB); break;
|
|
case 'F': USE_BITS (INSMSB); break;
|
|
case 'G': USE_BITS (EXTMSBD); break;
|
|
case 'H': USE_BITS (EXTMSBD); break;
|
|
default:
|
|
as_bad (_("Internal error: bad mips opcode "
|
|
"(unknown extension operand type `%c%c'): %s %s"),
|
|
e, c, opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
break;
|
|
case 'm':
|
|
e = c;
|
|
switch (c = *p++)
|
|
{
|
|
case 'A': USE_BITS (IMMA); break;
|
|
case 'B': USE_BITS (IMMB); break;
|
|
case 'C': USE_BITS (IMMC); break;
|
|
case 'D': USE_BITS (IMMD); break;
|
|
case 'E': USE_BITS (IMME); break;
|
|
case 'F': USE_BITS (IMMF); break;
|
|
case 'G': USE_BITS (IMMG); break;
|
|
case 'H': USE_BITS (IMMH); break;
|
|
case 'I': USE_BITS (IMMI); break;
|
|
case 'J': USE_BITS (IMMJ); break;
|
|
case 'L': USE_BITS (IMML); break;
|
|
case 'M': USE_BITS (IMMM); break;
|
|
case 'N': USE_BITS (IMMN); break;
|
|
case 'O': USE_BITS (IMMO); break;
|
|
case 'P': USE_BITS (IMMP); break;
|
|
case 'Q': USE_BITS (IMMQ); break;
|
|
case 'U': USE_BITS (IMMU); break;
|
|
case 'W': USE_BITS (IMMW); break;
|
|
case 'X': USE_BITS (IMMX); break;
|
|
case 'Y': USE_BITS (IMMY); break;
|
|
case 'Z': break;
|
|
case 'a': break;
|
|
case 'b': USE_BITS (MB); break;
|
|
case 'c': USE_BITS (MC); break;
|
|
case 'd': USE_BITS (MD); break;
|
|
case 'e': USE_BITS (ME); break;
|
|
case 'f': USE_BITS (MF); break;
|
|
case 'g': USE_BITS (MG); break;
|
|
case 'h': USE_BITS (MH); break;
|
|
case 'i': USE_BITS (MI); break;
|
|
case 'j': USE_BITS (MJ); break;
|
|
case 'l': USE_BITS (ML); break;
|
|
case 'm': USE_BITS (MM); break;
|
|
case 'n': USE_BITS (MN); break;
|
|
case 'p': USE_BITS (MP); break;
|
|
case 'q': USE_BITS (MQ); break;
|
|
case 'r': break;
|
|
case 's': break;
|
|
case 't': break;
|
|
case 'x': break;
|
|
case 'y': break;
|
|
case 'z': break;
|
|
default:
|
|
as_bad (_("Internal error: bad mips opcode "
|
|
"(unknown extension operand type `%c%c'): %s %s"),
|
|
e, c, opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
break;
|
|
case '.': USE_BITS (OFFSET10); break;
|
|
case '1': USE_BITS (STYPE); break;
|
|
case '<': USE_BITS (SHAMT); break;
|
|
case '>': USE_BITS (SHAMT); break;
|
|
case 'B': USE_BITS (CODE10); break;
|
|
case 'C': USE_BITS (COPZ); break;
|
|
case 'D': USE_BITS (FD); break;
|
|
case 'E': USE_BITS (RT); break;
|
|
case 'G': USE_BITS (RS); break;
|
|
case 'H': USE_BITS (SEL); break;
|
|
case 'K': USE_BITS (RS); break;
|
|
case 'M': USE_BITS (CCC); break;
|
|
case 'N': USE_BITS (BCC); break;
|
|
case 'R': USE_BITS (FR); break;
|
|
case 'S': USE_BITS (FS); break;
|
|
case 'T': USE_BITS (FT); break;
|
|
case 'V': USE_BITS (FS); break;
|
|
case '\\': USE_BITS (3BITPOS); break;
|
|
case 'a': USE_BITS (TARGET); break;
|
|
case 'b': USE_BITS (RS); break;
|
|
case 'c': USE_BITS (CODE); break;
|
|
case 'd': USE_BITS (RD); break;
|
|
case 'h': USE_BITS (PREFX); break;
|
|
case 'i': USE_BITS (IMMEDIATE); break;
|
|
case 'j': USE_BITS (DELTA); break;
|
|
case 'k': USE_BITS (CACHE); break;
|
|
case 'n': USE_BITS (RT); break;
|
|
case 'o': USE_BITS (DELTA); break;
|
|
case 'p': USE_BITS (DELTA); break;
|
|
case 'q': USE_BITS (CODE2); break;
|
|
case 'r': USE_BITS (RS); break;
|
|
case 's': USE_BITS (RS); break;
|
|
case 't': USE_BITS (RT); break;
|
|
case 'u': USE_BITS (IMMEDIATE); break;
|
|
case 'v': USE_BITS (RS); break;
|
|
case 'w': USE_BITS (RT); break;
|
|
case 'y': USE_BITS (RS3); break;
|
|
case 'z': break;
|
|
case '|': USE_BITS (TRAP); break;
|
|
case '~': USE_BITS (OFFSET12); break;
|
|
default:
|
|
as_bad (_("Internal error: bad microMIPS opcode "
|
|
"(unknown operand type `%c'): %s %s"),
|
|
c, opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
#undef USE_BITS
|
|
if (used_bits != insn_bits)
|
|
{
|
|
if (~used_bits & insn_bits)
|
|
as_bad (_("Internal error: bad microMIPS opcode "
|
|
"(bits 0x%lx undefined): %s %s"),
|
|
~used_bits & insn_bits, opc->name, opc->args);
|
|
if (used_bits & ~insn_bits)
|
|
as_bad (_("Internal error: bad microMIPS opcode "
|
|
"(bits 0x%lx defined): %s %s"),
|
|
used_bits & ~insn_bits, opc->name, opc->args);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
/* UDI immediates. */
|
/* UDI immediates. */
|
struct mips_immed {
|
struct mips_immed {
|
char type;
|
char type;
|
unsigned int shift;
|
unsigned int shift;
|
unsigned long mask;
|
unsigned long mask;
|
Line 8862... |
Line 10555... |
return 1;
|
return 1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
/* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
|
|
taking bits from BIT up. */
|
|
static int
|
|
expr_const_in_range (expressionS *ep, offsetT min, offsetT max, int bit)
|
|
{
|
|
return (ep->X_op == O_constant
|
|
&& (ep->X_add_number & ((1 << bit) - 1)) == 0
|
|
&& ep->X_add_number >= min << bit
|
|
&& ep->X_add_number < max << bit);
|
|
}
|
|
|
/* This routine assembles an instruction into its binary format. As a
|
/* This routine assembles an instruction into its binary format. As a
|
side effect, it sets one of the global variables imm_reloc or
|
side effect, it sets one of the global variables imm_reloc or
|
offset_reloc to the type of relocation to do if one of the operands
|
offset_reloc to the type of relocation to do if one of the operands
|
is an address expression. */
|
is an address expression. */
|
|
|
static void
|
static void
|
mips_ip (char *str, struct mips_cl_insn *ip)
|
mips_ip (char *str, struct mips_cl_insn *ip)
|
{
|
{
|
|
bfd_boolean wrong_delay_slot_insns = FALSE;
|
|
bfd_boolean need_delay_slot_ok = TRUE;
|
|
struct mips_opcode *firstinsn = NULL;
|
|
const struct mips_opcode *past;
|
|
struct hash_control *hash;
|
char *s;
|
char *s;
|
const char *args;
|
const char *args;
|
char c = 0;
|
char c = 0;
|
struct mips_opcode *insn;
|
struct mips_opcode *insn;
|
char *argsStart;
|
char *argsStart;
|
unsigned int regno;
|
unsigned int regno;
|
unsigned int lastregno;
|
unsigned int lastregno;
|
|
unsigned int destregno = 0;
|
unsigned int lastpos = 0;
|
unsigned int lastpos = 0;
|
unsigned int limlo, limhi;
|
unsigned int limlo, limhi;
|
char *s_reset;
|
char *s_reset;
|
char save_c = 0;
|
|
offsetT min_range, max_range;
|
offsetT min_range, max_range;
|
|
long opend;
|
|
char *name;
|
int argnum;
|
int argnum;
|
unsigned int rtype;
|
unsigned int rtype;
|
|
char *dot;
|
|
long end;
|
|
|
insn_error = NULL;
|
insn_error = NULL;
|
|
|
/* If the instruction contains a '.', we first try to match an instruction
|
if (mips_opts.micromips)
|
including the '.'. Then we try again without the '.'. */
|
|
insn = NULL;
|
|
for (s = str; *s != '\0' && !ISSPACE (*s); ++s)
|
|
continue;
|
|
|
|
/* If we stopped on whitespace, then replace the whitespace with null for
|
|
the call to hash_find. Save the character we replaced just in case we
|
|
have to re-parse the instruction. */
|
|
if (ISSPACE (*s))
|
|
{
|
{
|
save_c = *s;
|
hash = micromips_op_hash;
|
*s++ = '\0';
|
past = µmips_opcodes[bfd_micromips_num_opcodes];
|
}
|
}
|
|
else
|
insn = (struct mips_opcode *) hash_find (op_hash, str);
|
|
|
|
/* If we didn't find the instruction in the opcode table, try again, but
|
|
this time with just the instruction up to, but not including the
|
|
first '.'. */
|
|
if (insn == NULL)
|
|
{
|
{
|
/* Restore the character we overwrite above (if any). */
|
hash = op_hash;
|
if (save_c)
|
past = &mips_opcodes[NUMOPCODES];
|
*(--s) = save_c;
|
}
|
|
forced_insn_length = 0;
|
/* Scan up to the first '.' or whitespace. */
|
insn = NULL;
|
for (s = str;
|
|
*s != '\0' && *s != '.' && !ISSPACE (*s);
|
/* We first try to match an instruction up to a space or to the end. */
|
++s)
|
for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
|
continue;
|
continue;
|
|
|
/* If we did not find a '.', then we can quit now. */
|
/* Make a copy of the instruction so that we can fiddle with it. */
|
if (*s != '.')
|
name = alloca (end + 1);
|
|
memcpy (name, str, end);
|
|
name[end] = '\0';
|
|
|
|
for (;;)
|
{
|
{
|
insn_error = _("Unrecognized opcode");
|
insn = (struct mips_opcode *) hash_find (hash, name);
|
return;
|
|
}
|
|
|
|
/* Lookup the instruction in the hash table. */
|
if (insn != NULL || !mips_opts.micromips)
|
*s++ = '\0';
|
break;
|
if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
|
if (forced_insn_length)
|
|
break;
|
|
|
|
/* See if there's an instruction size override suffix,
|
|
either `16' or `32', at the end of the mnemonic proper,
|
|
that defines the operation, i.e. before the first `.'
|
|
character if any. Strip it and retry. */
|
|
dot = strchr (name, '.');
|
|
opend = dot != NULL ? dot - name : end;
|
|
if (opend < 3)
|
|
break;
|
|
if (name[opend - 2] == '1' && name[opend - 1] == '6')
|
|
forced_insn_length = 2;
|
|
else if (name[opend - 2] == '3' && name[opend - 1] == '2')
|
|
forced_insn_length = 4;
|
|
else
|
|
break;
|
|
memcpy (name + opend - 2, name + opend, end - opend + 1);
|
|
}
|
|
if (insn == NULL)
|
{
|
{
|
insn_error = _("Unrecognized opcode");
|
insn_error = _("Unrecognized opcode");
|
return;
|
return;
|
}
|
}
|
}
|
|
|
|
argsStart = s;
|
/* For microMIPS instructions placed in a fixed-length branch delay slot
|
|
we make up to two passes over the relevant fragment of the opcode
|
|
table. First we try instructions that meet the delay slot's length
|
|
requirement. If none matched, then we retry with the remaining ones
|
|
and if one matches, then we use it and then issue an appropriate
|
|
warning later on. */
|
|
argsStart = s = str + end;
|
for (;;)
|
for (;;)
|
{
|
{
|
|
bfd_boolean delay_slot_ok;
|
|
bfd_boolean size_ok;
|
bfd_boolean ok;
|
bfd_boolean ok;
|
|
|
gas_assert (strcmp (insn->name, str) == 0);
|
gas_assert (strcmp (insn->name, name) == 0);
|
|
|
ok = is_opcode_valid (insn);
|
ok = is_opcode_valid (insn);
|
if (! ok)
|
size_ok = is_size_valid (insn);
|
|
delay_slot_ok = is_delay_slot_valid (insn);
|
|
if (!delay_slot_ok && !wrong_delay_slot_insns)
|
{
|
{
|
if (insn + 1 < &mips_opcodes[NUMOPCODES]
|
firstinsn = insn;
|
&& strcmp (insn->name, insn[1].name) == 0)
|
wrong_delay_slot_insns = TRUE;
|
|
}
|
|
if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
|
|
{
|
|
static char buf[256];
|
|
|
|
if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
|
{
|
{
|
++insn;
|
++insn;
|
continue;
|
continue;
|
}
|
}
|
else
|
if (wrong_delay_slot_insns && need_delay_slot_ok)
|
{
|
|
if (!insn_error)
|
|
{
|
{
|
static char buf[100];
|
gas_assert (firstinsn);
|
sprintf (buf,
|
need_delay_slot_ok = FALSE;
|
_("opcode not supported on this processor: %s (%s)"),
|
past = insn + 1;
|
|
insn = firstinsn;
|
|
continue;
|
|
}
|
|
|
|
if (insn_error)
|
|
return;
|
|
|
|
if (!ok)
|
|
sprintf (buf, _("opcode not supported on this processor: %s (%s)"),
|
mips_cpu_info_from_arch (mips_opts.arch)->name,
|
mips_cpu_info_from_arch (mips_opts.arch)->name,
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
|
else
|
|
sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
|
|
8 * forced_insn_length);
|
insn_error = buf;
|
insn_error = buf;
|
}
|
|
if (save_c)
|
|
*(--s) = save_c;
|
|
return;
|
return;
|
}
|
}
|
}
|
|
|
|
create_insn (ip, insn);
|
create_insn (ip, insn);
|
insn_error = NULL;
|
insn_error = NULL;
|
argnum = 1;
|
argnum = 1;
|
lastregno = 0xffffffff;
|
lastregno = 0xffffffff;
|
Line 8986... |
Line 10727... |
if (*s == '\0')
|
if (*s == '\0')
|
return;
|
return;
|
break;
|
break;
|
|
|
case '2': /* DSP 2-bit unsigned immediate in bit 11. */
|
case '2': /* DSP 2-bit unsigned immediate in bit 11. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number != 1
|
if ((unsigned long) imm_expr.X_add_number != 1
|
&& (unsigned long) imm_expr.X_add_number != 3)
|
&& (unsigned long) imm_expr.X_add_number != 3)
|
{
|
{
|
as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
|
as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (BP, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, BP, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '3': /* DSP 3-bit unsigned immediate in bit 21. */
|
case '3': /* DSP 3-bit unsigned immediate in bit 21. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_SA3)
|
if (imm_expr.X_add_number & ~OP_MASK_SA3)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_SA3, (unsigned long) imm_expr.X_add_number);
|
OP_MASK_SA3, (unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (SA3, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, SA3, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '4': /* DSP 4-bit unsigned immediate in bit 21. */
|
case '4': /* DSP 4-bit unsigned immediate in bit 21. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_SA4)
|
if (imm_expr.X_add_number & ~OP_MASK_SA4)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_SA4, (unsigned long) imm_expr.X_add_number);
|
OP_MASK_SA4, (unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (SA4, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, SA4, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '5': /* DSP 8-bit unsigned immediate in bit 16. */
|
case '5': /* DSP 8-bit unsigned immediate in bit 16. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_IMM8)
|
if (imm_expr.X_add_number & ~OP_MASK_IMM8)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number);
|
OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (IMM8, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, IMM8, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '6': /* DSP 5-bit unsigned immediate in bit 21. */
|
case '6': /* DSP 5-bit unsigned immediate in bit 21. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_RS)
|
if (imm_expr.X_add_number & ~OP_MASK_RS)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_RS, (unsigned long) imm_expr.X_add_number);
|
OP_MASK_RS, (unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (RS, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, RS, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '7': /* Four DSP accumulators in bits 11,12. */
|
case '7': /* Four DSP accumulators in bits 11,12. */
|
|
gas_assert (!mips_opts.micromips);
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
s[3] >= '0' && s[3] <= '3')
|
s[3] >= '0' && s[3] <= '3')
|
{
|
{
|
regno = s[3] - '0';
|
regno = s[3] - '0';
|
s += 4;
|
s += 4;
|
INSERT_OPERAND (DSPACC, *ip, regno);
|
INSERT_OPERAND (0, DSPACC, *ip, regno);
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
as_bad (_("Invalid dsp acc register"));
|
as_bad (_("Invalid dsp acc register"));
|
break;
|
break;
|
|
|
case '8': /* DSP 6-bit unsigned immediate in bit 11. */
|
case '8': /* DSP 6-bit unsigned immediate in bit 11. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_WRDSP)
|
if (imm_expr.X_add_number & ~OP_MASK_WRDSP)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_WRDSP,
|
OP_MASK_WRDSP,
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (WRDSP, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, WRDSP, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '9': /* Four DSP accumulators in bits 21,22. */
|
case '9': /* Four DSP accumulators in bits 21,22. */
|
|
gas_assert (!mips_opts.micromips);
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
s[3] >= '0' && s[3] <= '3')
|
s[3] >= '0' && s[3] <= '3')
|
{
|
{
|
regno = s[3] - '0';
|
regno = s[3] - '0';
|
s += 4;
|
s += 4;
|
INSERT_OPERAND (DSPACC_S, *ip, regno);
|
INSERT_OPERAND (0, DSPACC_S, *ip, regno);
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
as_bad (_("Invalid dsp acc register"));
|
as_bad (_("Invalid dsp acc register"));
|
break;
|
break;
|
|
|
case '0': /* DSP 6-bit signed immediate in bit 20. */
|
case '0': /* DSP 6-bit signed immediate in bit 20. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_DSPSFT + 1) >> 1);
|
min_range = -((OP_MASK_DSPSFT + 1) >> 1);
|
max_range = ((OP_MASK_DSPSFT + 1) >> 1) - 1;
|
max_range = ((OP_MASK_DSPSFT + 1) >> 1) - 1;
|
if (imm_expr.X_add_number < min_range ||
|
if (imm_expr.X_add_number < min_range ||
|
Line 9103... |
Line 10853... |
{
|
{
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
(long) min_range, (long) max_range,
|
(long) min_range, (long) max_range,
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (DSPSFT, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, DSPSFT, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '\'': /* DSP 6-bit unsigned immediate in bit 16. */
|
case '\'': /* DSP 6-bit unsigned immediate in bit 16. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_RDDSP)
|
if (imm_expr.X_add_number & ~OP_MASK_RDDSP)
|
{
|
{
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
as_bad (_("DSP immediate not in range 0..%d (%lu)"),
|
OP_MASK_RDDSP,
|
OP_MASK_RDDSP,
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (RDDSP, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, RDDSP, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case ':': /* DSP 7-bit signed immediate in bit 19. */
|
case ':': /* DSP 7-bit signed immediate in bit 19. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1);
|
min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1);
|
max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1;
|
max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1;
|
if (imm_expr.X_add_number < min_range ||
|
if (imm_expr.X_add_number < min_range ||
|
Line 9134... |
Line 10886... |
{
|
{
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
(long) min_range, (long) max_range,
|
(long) min_range, (long) max_range,
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (DSPSFT_7, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, DSPSFT_7, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '@': /* DSP 10-bit signed immediate in bit 16. */
|
case '@': /* DSP 10-bit signed immediate in bit 16. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_IMM10 + 1) >> 1);
|
min_range = -((OP_MASK_IMM10 + 1) >> 1);
|
max_range = ((OP_MASK_IMM10 + 1) >> 1) - 1;
|
max_range = ((OP_MASK_IMM10 + 1) >> 1) - 1;
|
if (imm_expr.X_add_number < min_range ||
|
if (imm_expr.X_add_number < min_range ||
|
Line 9151... |
Line 10904... |
{
|
{
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
|
(long) min_range, (long) max_range,
|
(long) min_range, (long) max_range,
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (IMM10, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, IMM10, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '!': /* MT usermode flag bit. */
|
case '!': /* MT usermode flag bit. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_MT_U)
|
if (imm_expr.X_add_number & ~OP_MASK_MT_U)
|
as_bad (_("MT usermode bit not 0 or 1 (%lu)"),
|
as_bad (_("MT usermode bit not 0 or 1 (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (MT_U, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, MT_U, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '$': /* MT load high flag bit. */
|
case '$': /* MT load high flag bit. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number & ~OP_MASK_MT_H)
|
if (imm_expr.X_add_number & ~OP_MASK_MT_H)
|
as_bad (_("MT load high bit not 0 or 1 (%lu)"),
|
as_bad (_("MT load high bit not 0 or 1 (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (MT_H, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, MT_H, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '*': /* Four DSP accumulators in bits 18,19. */
|
case '*': /* Four DSP accumulators in bits 18,19. */
|
|
gas_assert (!mips_opts.micromips);
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
s[3] >= '0' && s[3] <= '3')
|
s[3] >= '0' && s[3] <= '3')
|
{
|
{
|
regno = s[3] - '0';
|
regno = s[3] - '0';
|
s += 4;
|
s += 4;
|
INSERT_OPERAND (MTACC_T, *ip, regno);
|
INSERT_OPERAND (0, MTACC_T, *ip, regno);
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
as_bad (_("Invalid dsp/smartmips acc register"));
|
as_bad (_("Invalid dsp/smartmips acc register"));
|
break;
|
break;
|
|
|
case '&': /* Four DSP accumulators in bits 13,14. */
|
case '&': /* Four DSP accumulators in bits 13,14. */
|
|
gas_assert (!mips_opts.micromips);
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
|
s[3] >= '0' && s[3] <= '3')
|
s[3] >= '0' && s[3] <= '3')
|
{
|
{
|
regno = s[3] - '0';
|
regno = s[3] - '0';
|
s += 4;
|
s += 4;
|
INSERT_OPERAND (MTACC_D, *ip, regno);
|
INSERT_OPERAND (0, MTACC_D, *ip, regno);
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
as_bad (_("Invalid dsp/smartmips acc register"));
|
as_bad (_("Invalid dsp/smartmips acc register"));
|
break;
|
break;
|
|
|
|
case '\\': /* 3-bit bit position. */
|
|
{
|
|
unsigned long mask = (!mips_opts.micromips
|
|
? OP_MASK_3BITPOS
|
|
: MICROMIPSOP_MASK_3BITPOS);
|
|
|
|
my_getExpression (&imm_expr, s);
|
|
check_absolute_expr (ip, &imm_expr);
|
|
if ((unsigned long) imm_expr.X_add_number > mask)
|
|
as_warn (_("Bit position for %s not in range 0..%lu (%lu)"),
|
|
ip->insn_mo->name,
|
|
mask, (unsigned long) imm_expr.X_add_number);
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
3BITPOS, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
|
s = expr_end;
|
|
}
|
|
continue;
|
|
|
case ',':
|
case ',':
|
++argnum;
|
++argnum;
|
if (*s++ == *args)
|
if (*s++ == *args)
|
continue;
|
continue;
|
s--;
|
s--;
|
switch (*++args)
|
switch (*++args)
|
{
|
{
|
case 'r':
|
case 'r':
|
case 'v':
|
case 'v':
|
INSERT_OPERAND (RS, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
|
continue;
|
continue;
|
|
|
case 'w':
|
case 'w':
|
INSERT_OPERAND (RT, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
|
continue;
|
continue;
|
|
|
case 'W':
|
case 'W':
|
INSERT_OPERAND (FT, *ip, lastregno);
|
gas_assert (!mips_opts.micromips);
|
|
INSERT_OPERAND (0, FT, *ip, lastregno);
|
continue;
|
continue;
|
|
|
case 'V':
|
case 'V':
|
INSERT_OPERAND (FS, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
|
continue;
|
continue;
|
}
|
}
|
break;
|
break;
|
|
|
case '(':
|
case '(':
|
/* Handle optional base register.
|
/* Handle optional base register.
|
Either the base register is omitted or
|
Either the base register is omitted or
|
we must have a left paren. */
|
we must have a left paren. */
|
/* This is dependent on the next operand specifier
|
/* This is dependent on the next operand specifier
|
is a base register specification. */
|
is a base register specification. */
|
gas_assert (args[1] == 'b');
|
gas_assert (args[1] == 'b'
|
if (*s == '\0')
|
|| (mips_opts.micromips
|
|
&& args[1] == 'm'
|
|
&& (args[2] == 'l' || args[2] == 'n'
|
|
|| args[2] == 's' || args[2] == 'a')));
|
|
if (*s == '\0' && args[1] == 'b')
|
return;
|
return;
|
|
/* Fall through. */
|
|
|
case ')': /* These must match exactly. */
|
case ')': /* These must match exactly. */
|
case '[':
|
if (*s++ == *args)
|
|
continue;
|
|
break;
|
|
|
|
case '[': /* These must match exactly. */
|
case ']':
|
case ']':
|
|
gas_assert (!mips_opts.micromips);
|
if (*s++ == *args)
|
if (*s++ == *args)
|
continue;
|
continue;
|
break;
|
break;
|
|
|
case '+': /* Opcode extension character. */
|
case '+': /* Opcode extension character. */
|
Line 9254... |
Line 11041... |
{
|
{
|
case '1': /* UDI immediates. */
|
case '1': /* UDI immediates. */
|
case '2':
|
case '2':
|
case '3':
|
case '3':
|
case '4':
|
case '4':
|
|
gas_assert (!mips_opts.micromips);
|
{
|
{
|
const struct mips_immed *imm = mips_immed;
|
const struct mips_immed *imm = mips_immed;
|
|
|
while (imm->type && imm->type != *args)
|
while (imm->type && imm->type != *args)
|
++imm;
|
++imm;
|
Line 9297... |
Line 11085... |
as_bad (_("Improper position (%lu)"),
|
as_bad (_("Improper position (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number = limlo;
|
imm_expr.X_add_number = limlo;
|
}
|
}
|
lastpos = imm_expr.X_add_number;
|
lastpos = imm_expr.X_add_number;
|
INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
EXTLSB, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'B': /* ins size, becomes MSB. */
|
case 'B': /* ins size, becomes MSB. */
|
Line 9328... |
Line 11117... |
as_bad (_("Improper insert size (%lu, position %lu)"),
|
as_bad (_("Improper insert size (%lu, position %lu)"),
|
(unsigned long) imm_expr.X_add_number,
|
(unsigned long) imm_expr.X_add_number,
|
(unsigned long) lastpos);
|
(unsigned long) lastpos);
|
imm_expr.X_add_number = limlo - lastpos;
|
imm_expr.X_add_number = limlo - lastpos;
|
}
|
}
|
INSERT_OPERAND (INSMSB, *ip,
|
INSERT_OPERAND (mips_opts.micromips, INSMSB, *ip,
|
lastpos + imm_expr.X_add_number - 1);
|
lastpos + imm_expr.X_add_number - 1);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
Line 9364... |
Line 11153... |
as_bad (_("Improper extract size (%lu, position %lu)"),
|
as_bad (_("Improper extract size (%lu, position %lu)"),
|
(unsigned long) imm_expr.X_add_number,
|
(unsigned long) imm_expr.X_add_number,
|
(unsigned long) lastpos);
|
(unsigned long) lastpos);
|
imm_expr.X_add_number = limlo - lastpos;
|
imm_expr.X_add_number = limlo - lastpos;
|
}
|
}
|
INSERT_OPERAND (EXTMSBD, *ip, imm_expr.X_add_number - 1);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
EXTMSBD, *ip, imm_expr.X_add_number - 1);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'D':
|
case 'D':
|
Line 9385... |
Line 11175... |
normalize_constant_expr (&imm2_expr);
|
normalize_constant_expr (&imm2_expr);
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'T': /* Coprocessor register. */
|
case 'T': /* Coprocessor register. */
|
|
gas_assert (!mips_opts.micromips);
|
/* +T is for disassembly only; never match. */
|
/* +T is for disassembly only; never match. */
|
break;
|
break;
|
|
|
case 't': /* Coprocessor register number. */
|
case 't': /* Coprocessor register number. */
|
|
gas_assert (!mips_opts.micromips);
|
if (s[0] == '$' && ISDIGIT (s[1]))
|
if (s[0] == '$' && ISDIGIT (s[1]))
|
{
|
{
|
++s;
|
++s;
|
regno = 0;
|
regno = 0;
|
do
|
do
|
Line 9404... |
Line 11196... |
while (ISDIGIT (*s));
|
while (ISDIGIT (*s));
|
if (regno > 31)
|
if (regno > 31)
|
as_bad (_("Invalid register number (%d)"), regno);
|
as_bad (_("Invalid register number (%d)"), regno);
|
else
|
else
|
{
|
{
|
INSERT_OPERAND (RT, *ip, regno);
|
INSERT_OPERAND (0, RT, *ip, regno);
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
else
|
else
|
as_bad (_("Invalid coprocessor 0 register number"));
|
as_bad (_("Invalid coprocessor 0 register number"));
|
break;
|
break;
|
|
|
case 'x':
|
case 'x':
|
/* bbit[01] and bbit[01]32 bit index. Give error if index
|
/* bbit[01] and bbit[01]32 bit index. Give error if index
|
is not in the valid range. */
|
is not in the valid range. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned) imm_expr.X_add_number > 31)
|
if ((unsigned) imm_expr.X_add_number > 31)
|
{
|
{
|
as_bad (_("Improper bit index (%lu)"),
|
as_bad (_("Improper bit index (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number = 0;
|
imm_expr.X_add_number = 0;
|
}
|
}
|
INSERT_OPERAND (BBITIND, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'X':
|
case 'X':
|
/* bbit[01] bit index when bbit is used but we generate
|
/* bbit[01] bit index when bbit is used but we generate
|
bbit[01]32 because the index is over 32. Move to the
|
bbit[01]32 because the index is over 32. Move to the
|
next candidate if index is not in the valid range. */
|
next candidate if index is not in the valid range. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned) imm_expr.X_add_number < 32
|
if ((unsigned) imm_expr.X_add_number < 32
|
|| (unsigned) imm_expr.X_add_number > 63)
|
|| (unsigned) imm_expr.X_add_number > 63)
|
break;
|
break;
|
INSERT_OPERAND (BBITIND, *ip, imm_expr.X_add_number - 32);
|
INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number - 32);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'p':
|
case 'p':
|
/* cins, cins32, exts and exts32 position field. Give error
|
/* cins, cins32, exts and exts32 position field. Give error
|
if it's not in the valid range. */
|
if it's not in the valid range. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned) imm_expr.X_add_number > 31)
|
if ((unsigned) imm_expr.X_add_number > 31)
|
{
|
{
|
as_bad (_("Improper position (%lu)"),
|
as_bad (_("Improper position (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number = 0;
|
imm_expr.X_add_number = 0;
|
}
|
}
|
/* Make the pos explicit to simplify +S. */
|
/* Make the pos explicit to simplify +S. */
|
lastpos = imm_expr.X_add_number + 32;
|
lastpos = imm_expr.X_add_number + 32;
|
INSERT_OPERAND (CINSPOS, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'P':
|
case 'P':
|
/* cins, cins32, exts and exts32 position field. Move to
|
/* cins, cins32, exts and exts32 position field. Move to
|
the next candidate if it's not in the valid range. */
|
the next candidate if it's not in the valid range. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned) imm_expr.X_add_number < 32
|
if ((unsigned) imm_expr.X_add_number < 32
|
|| (unsigned) imm_expr.X_add_number > 63)
|
|| (unsigned) imm_expr.X_add_number > 63)
|
break;
|
break;
|
lastpos = imm_expr.X_add_number;
|
lastpos = imm_expr.X_add_number;
|
INSERT_OPERAND (CINSPOS, *ip, imm_expr.X_add_number - 32);
|
INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number - 32);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 's':
|
case 's':
|
/* cins and exts length-minus-one field. */
|
/* cins and exts length-minus-one field. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
{
|
{
|
as_bad (_("Improper size (%lu)"),
|
as_bad (_("Improper size (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number = 0;
|
imm_expr.X_add_number = 0;
|
}
|
}
|
INSERT_OPERAND (CINSLM1, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'S':
|
case 'S':
|
/* cins32/exts32 and cins/exts aliasing cint32/exts32
|
/* cins32/exts32 and cins/exts aliasing cint32/exts32
|
length-minus-one field. */
|
length-minus-one field. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((long) imm_expr.X_add_number < 0
|
if ((long) imm_expr.X_add_number < 0
|
|| (unsigned long) imm_expr.X_add_number + lastpos > 63)
|
|| (unsigned long) imm_expr.X_add_number + lastpos > 63)
|
{
|
{
|
as_bad (_("Improper size (%lu)"),
|
as_bad (_("Improper size (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number = 0;
|
imm_expr.X_add_number = 0;
|
}
|
}
|
INSERT_OPERAND (CINSLM1, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'Q':
|
case 'Q':
|
/* seqi/snei immediate field. */
|
/* seqi/snei immediate field. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((long) imm_expr.X_add_number < -512
|
if ((long) imm_expr.X_add_number < -512
|
|| (long) imm_expr.X_add_number >= 512)
|
|| (long) imm_expr.X_add_number >= 512)
|
{
|
{
|
as_bad (_("Improper immediate (%ld)"),
|
as_bad (_("Improper immediate (%ld)"),
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
imm_expr.X_add_number = 0;
|
imm_expr.X_add_number = 0;
|
}
|
}
|
INSERT_OPERAND (SEQI, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, SEQI, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'a': /* 8-bit signed offset in bit 6 */
|
case 'a': /* 8-bit signed offset in bit 6 */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_OFFSET_A + 1) >> 1);
|
min_range = -((OP_MASK_OFFSET_A + 1) >> 1);
|
max_range = ((OP_MASK_OFFSET_A + 1) >> 1) - 1;
|
max_range = ((OP_MASK_OFFSET_A + 1) >> 1) - 1;
|
if (imm_expr.X_add_number < min_range
|
if (imm_expr.X_add_number < min_range
|
Line 9534... |
Line 11334... |
{
|
{
|
as_bad (_("Offset not in range %ld..%ld (%ld)"),
|
as_bad (_("Offset not in range %ld..%ld (%ld)"),
|
(long) min_range, (long) max_range,
|
(long) min_range, (long) max_range,
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (OFFSET_A, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, OFFSET_A, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'b': /* 8-bit signed offset in bit 3 */
|
case 'b': /* 8-bit signed offset in bit 3 */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_OFFSET_B + 1) >> 1);
|
min_range = -((OP_MASK_OFFSET_B + 1) >> 1);
|
max_range = ((OP_MASK_OFFSET_B + 1) >> 1) - 1;
|
max_range = ((OP_MASK_OFFSET_B + 1) >> 1) - 1;
|
if (imm_expr.X_add_number < min_range
|
if (imm_expr.X_add_number < min_range
|
Line 9551... |
Line 11352... |
{
|
{
|
as_bad (_("Offset not in range %ld..%ld (%ld)"),
|
as_bad (_("Offset not in range %ld..%ld (%ld)"),
|
(long) min_range, (long) max_range,
|
(long) min_range, (long) max_range,
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
INSERT_OPERAND (OFFSET_B, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, OFFSET_B, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'c': /* 9-bit signed offset in bit 6 */
|
case 'c': /* 9-bit signed offset in bit 6 */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
|
min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
|
max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
|
max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
|
/* We check the offset range before adjusted. */
|
/* We check the offset range before adjusted. */
|
Line 9577... |
Line 11379... |
{
|
{
|
as_bad (_("Offset not 16 bytes alignment (%ld)"),
|
as_bad (_("Offset not 16 bytes alignment (%ld)"),
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
}
|
}
|
/* Right shift 4 bits to adjust the offset operand. */
|
/* Right shift 4 bits to adjust the offset operand. */
|
INSERT_OPERAND (OFFSET_C, *ip, imm_expr.X_add_number >> 4);
|
INSERT_OPERAND (0, OFFSET_C, *ip,
|
|
imm_expr.X_add_number >> 4);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'z':
|
case 'z':
|
|
gas_assert (!mips_opts.micromips);
|
if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
|
if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
|
break;
|
break;
|
if (regno == AT && mips_opts.at)
|
if (regno == AT && mips_opts.at)
|
{
|
{
|
if (mips_opts.at == ATREG)
|
if (mips_opts.at == ATREG)
|
as_warn (_("used $at without \".set noat\""));
|
as_warn (_("used $at without \".set noat\""));
|
else
|
else
|
as_warn (_("used $%u with \".set at=$%u\""),
|
as_warn (_("used $%u with \".set at=$%u\""),
|
regno, mips_opts.at);
|
regno, mips_opts.at);
|
}
|
}
|
INSERT_OPERAND (RZ, *ip, regno);
|
INSERT_OPERAND (0, RZ, *ip, regno);
|
continue;
|
continue;
|
|
|
case 'Z':
|
case 'Z':
|
|
gas_assert (!mips_opts.micromips);
|
if (!reg_lookup (&s, RTYPE_FPU, ®no))
|
if (!reg_lookup (&s, RTYPE_FPU, ®no))
|
break;
|
break;
|
INSERT_OPERAND (FZ, *ip, regno);
|
INSERT_OPERAND (0, FZ, *ip, regno);
|
continue;
|
continue;
|
|
|
default:
|
default:
|
as_bad (_("Internal error: bad mips opcode "
|
as_bad (_("Internal error: bad %s opcode "
|
"(unknown extension operand type `+%c'): %s %s"),
|
"(unknown extension operand type `+%c'): %s %s"),
|
|
mips_opts.micromips ? "microMIPS" : "MIPS",
|
*args, insn->name, insn->args);
|
*args, insn->name, insn->args);
|
/* Further processing is fruitless. */
|
/* Further processing is fruitless. */
|
return;
|
return;
|
}
|
}
|
break;
|
break;
|
|
|
|
case '.': /* 10-bit offset. */
|
|
gas_assert (mips_opts.micromips);
|
|
case '~': /* 12-bit offset. */
|
|
{
|
|
int shift = *args == '.' ? 9 : 11;
|
|
size_t i;
|
|
|
|
/* Check whether there is only a single bracketed expression
|
|
left. If so, it must be the base register and the
|
|
constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
continue;
|
|
|
|
/* If this value won't fit into the offset, then go find
|
|
a macro that will generate a 16- or 32-bit offset code
|
|
pattern. */
|
|
i = my_getSmallExpression (&imm_expr, imm_reloc, s);
|
|
if ((i == 0 && (imm_expr.X_op != O_constant
|
|
|| imm_expr.X_add_number >= 1 << shift
|
|
|| imm_expr.X_add_number < -1 << shift))
|
|
|| i > 0)
|
|
{
|
|
imm_expr.X_op = O_absent;
|
|
break;
|
|
}
|
|
if (shift == 9)
|
|
INSERT_OPERAND (1, OFFSET10, *ip, imm_expr.X_add_number);
|
|
else
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
OFFSET12, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
|
s = expr_end;
|
|
}
|
|
continue;
|
|
|
case '<': /* must be at least one digit */
|
case '<': /* must be at least one digit */
|
/*
|
/*
|
* According to the manual, if the shift amount is greater
|
* According to the manual, if the shift amount is greater
|
* than 31 or less than 0, then the shift amount should be
|
* than 31 or less than 0, then the shift amount should be
|
* mod 32. In reality the mips assembler issues an error.
|
* mod 32. In reality the mips assembler issues an error.
|
Line 9623... |
Line 11464... |
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
as_warn (_("Improper shift amount (%lu)"),
|
as_warn (_("Improper shift amount (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
SHAMT, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case '>': /* shift amount minus 32 */
|
case '>': /* shift amount minus 32 */
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number < 32
|
if ((unsigned long) imm_expr.X_add_number < 32
|
|| (unsigned long) imm_expr.X_add_number > 63)
|
|| (unsigned long) imm_expr.X_add_number > 63)
|
break;
|
break;
|
INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number - 32);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
SHAMT, *ip, imm_expr.X_add_number - 32);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'k': /* CACHE code. */
|
case 'k': /* CACHE code. */
|
Line 9648... |
Line 11491... |
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
if ((unsigned long) imm_expr.X_add_number > 31)
|
as_warn (_("Invalid value for `%s' (%lu)"),
|
as_warn (_("Invalid value for `%s' (%lu)"),
|
ip->insn_mo->name,
|
ip->insn_mo->name,
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
if (*args == 'k')
|
switch (*args)
|
{
|
{
|
if (mips_fix_cn63xxp1 && strcmp ("pref", insn->name) == 0)
|
case 'k':
|
|
if (mips_fix_cn63xxp1
|
|
&& !mips_opts.micromips
|
|
&& strcmp ("pref", insn->name) == 0)
|
switch (imm_expr.X_add_number)
|
switch (imm_expr.X_add_number)
|
{
|
{
|
case 5:
|
case 5:
|
case 25:
|
case 25:
|
case 26:
|
case 26:
|
Line 9667... |
Line 11513... |
|
|
default: /* The rest must be changed to 28. */
|
default: /* The rest must be changed to 28. */
|
imm_expr.X_add_number = 28;
|
imm_expr.X_add_number = 28;
|
break;
|
break;
|
}
|
}
|
INSERT_OPERAND (CACHE, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
CACHE, *ip, imm_expr.X_add_number);
|
|
break;
|
|
case 'h':
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
PREFX, *ip, imm_expr.X_add_number);
|
|
break;
|
|
case '1':
|
|
INSERT_OPERAND (mips_opts.micromips,
|
|
STYPE, *ip, imm_expr.X_add_number);
|
|
break;
|
}
|
}
|
else if (*args == 'h')
|
|
INSERT_OPERAND (PREFX, *ip, imm_expr.X_add_number);
|
|
else
|
|
INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'c': /* BREAK code. */
|
case 'c': /* BREAK code. */
|
|
{
|
|
unsigned long mask = (mips_opts.micromips
|
|
? MICROMIPSOP_MASK_CODE
|
|
: OP_MASK_CODE);
|
|
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE)
|
if ((unsigned long) imm_expr.X_add_number > mask)
|
as_warn (_("Code for %s not in range 0..1023 (%lu)"),
|
as_warn (_("Code for %s not in range 0..%lu (%lu)"),
|
ip->insn_mo->name,
|
ip->insn_mo->name,
|
(unsigned long) imm_expr.X_add_number);
|
mask, (unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (CODE, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
CODE, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
|
}
|
continue;
|
continue;
|
|
|
case 'q': /* Lower BREAK code. */
|
case 'q': /* Lower BREAK code. */
|
|
{
|
|
unsigned long mask = (mips_opts.micromips
|
|
? MICROMIPSOP_MASK_CODE2
|
|
: OP_MASK_CODE2);
|
|
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE2)
|
if ((unsigned long) imm_expr.X_add_number > mask)
|
as_warn (_("Lower code for %s not in range 0..1023 (%lu)"),
|
as_warn (_("Lower code for %s not in range 0..%lu (%lu)"),
|
ip->insn_mo->name,
|
ip->insn_mo->name,
|
(unsigned long) imm_expr.X_add_number);
|
mask, (unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (CODE2, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (mips_opts.micromips,
|
|
CODE2, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
|
}
|
continue;
|
continue;
|
|
|
case 'B': /* 20-bit SYSCALL/BREAK code. */
|
case 'B': /* 20- or 10-bit syscall/break/wait code. */
|
|
{
|
|
unsigned long mask = (mips_opts.micromips
|
|
? MICROMIPSOP_MASK_CODE10
|
|
: OP_MASK_CODE20);
|
|
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20)
|
if ((unsigned long) imm_expr.X_add_number > mask)
|
as_warn (_("Code for %s not in range 0..1048575 (%lu)"),
|
as_warn (_("Code for %s not in range 0..%lu (%lu)"),
|
ip->insn_mo->name,
|
ip->insn_mo->name,
|
(unsigned long) imm_expr.X_add_number);
|
mask, (unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (CODE20, *ip, imm_expr.X_add_number);
|
if (mips_opts.micromips)
|
|
INSERT_OPERAND (1, CODE10, *ip, imm_expr.X_add_number);
|
|
else
|
|
INSERT_OPERAND (0, CODE20, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
|
}
|
continue;
|
continue;
|
|
|
case 'C': /* Coprocessor code. */
|
case 'C': /* 25- or 23-bit coprocessor code. */
|
|
{
|
|
unsigned long mask = (mips_opts.micromips
|
|
? MICROMIPSOP_MASK_COPZ
|
|
: OP_MASK_COPZ);
|
|
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_COPZ)
|
if ((unsigned long) imm_expr.X_add_number > mask)
|
{
|
as_warn (_("Coproccesor code > %u bits (%lu)"),
|
as_warn (_("Coproccesor code > 25 bits (%lu)"),
|
mips_opts.micromips ? 23U : 25U,
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number &= OP_MASK_COPZ;
|
INSERT_OPERAND (mips_opts.micromips,
|
}
|
COPZ, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (COPZ, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
|
}
|
continue;
|
continue;
|
|
|
case 'J': /* 19-bit WAIT code. */
|
case 'J': /* 19-bit WAIT code. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
|
{
|
{
|
as_warn (_("Illegal 19-bit code (%lu)"),
|
as_warn (_("Illegal 19-bit code (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
imm_expr.X_add_number &= OP_MASK_CODE19;
|
imm_expr.X_add_number &= OP_MASK_CODE19;
|
}
|
}
|
INSERT_OPERAND (CODE19, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, CODE19, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'P': /* Performance register. */
|
case 'P': /* Performance register. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
|
if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
|
as_warn (_("Invalid performance register (%lu)"),
|
as_warn (_("Invalid performance register (%lu)"),
|
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
INSERT_OPERAND (PERFREG, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'G': /* Coprocessor destination register. */
|
case 'G': /* Coprocessor destination register. */
|
if (((ip->insn_opcode >> OP_SH_OP) & OP_MASK_OP) == OP_OP_COP0)
|
{
|
ok = reg_lookup (&s, RTYPE_NUM | RTYPE_CP0, ®no);
|
unsigned long opcode = ip->insn_opcode;
|
|
unsigned long mask;
|
|
unsigned int types;
|
|
int cop0;
|
|
|
|
if (mips_opts.micromips)
|
|
{
|
|
mask = ~((MICROMIPSOP_MASK_RT << MICROMIPSOP_SH_RT)
|
|
| (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS)
|
|
| (MICROMIPSOP_MASK_SEL << MICROMIPSOP_SH_SEL));
|
|
opcode &= mask;
|
|
switch (opcode)
|
|
{
|
|
case 0x000000fc: /* mfc0 */
|
|
case 0x000002fc: /* mtc0 */
|
|
case 0x580000fc: /* dmfc0 */
|
|
case 0x580002fc: /* dmtc0 */
|
|
cop0 = 1;
|
|
break;
|
|
default:
|
|
cop0 = 0;
|
|
break;
|
|
}
|
|
}
|
else
|
else
|
ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
|
{
|
INSERT_OPERAND (RD, *ip, regno);
|
opcode = (opcode >> OP_SH_OP) & OP_MASK_OP;
|
|
cop0 = opcode == OP_OP_COP0;
|
|
}
|
|
types = RTYPE_NUM | (cop0 ? RTYPE_CP0 : RTYPE_GP);
|
|
ok = reg_lookup (&s, types, ®no);
|
|
if (mips_opts.micromips)
|
|
INSERT_OPERAND (1, RS, *ip, regno);
|
|
else
|
|
INSERT_OPERAND (0, RD, *ip, regno);
|
if (ok)
|
if (ok)
|
{
|
{
|
lastregno = regno;
|
lastregno = regno;
|
continue;
|
continue;
|
}
|
}
|
else
|
}
|
break;
|
break;
|
|
|
|
case 'y': /* ALNV.PS source register. */
|
|
gas_assert (mips_opts.micromips);
|
|
goto do_reg;
|
|
case 'x': /* Ignore register name. */
|
|
case 'U': /* Destination register (CLO/CLZ). */
|
|
case 'g': /* Coprocessor destination register. */
|
|
gas_assert (!mips_opts.micromips);
|
case 'b': /* Base register. */
|
case 'b': /* Base register. */
|
case 'd': /* Destination register. */
|
case 'd': /* Destination register. */
|
case 's': /* Source register. */
|
case 's': /* Source register. */
|
case 't': /* Target register. */
|
case 't': /* Target register. */
|
case 'r': /* Both target and source. */
|
case 'r': /* Both target and source. */
|
case 'v': /* Both dest and source. */
|
case 'v': /* Both dest and source. */
|
case 'w': /* Both dest and target. */
|
case 'w': /* Both dest and target. */
|
case 'E': /* Coprocessor target register. */
|
case 'E': /* Coprocessor target register. */
|
case 'K': /* RDHWR destination register. */
|
case 'K': /* RDHWR destination register. */
|
case 'x': /* Ignore register name. */
|
|
case 'z': /* Must be zero register. */
|
case 'z': /* Must be zero register. */
|
case 'U': /* Destination register (CLO/CLZ). */
|
do_reg:
|
case 'g': /* Coprocessor destination register. */
|
|
s_reset = s;
|
s_reset = s;
|
if (*args == 'E' || *args == 'K')
|
if (*args == 'E' || *args == 'K')
|
ok = reg_lookup (&s, RTYPE_NUM, ®no);
|
ok = reg_lookup (&s, RTYPE_NUM, ®no);
|
else
|
else
|
{
|
{
|
Line 9835... |
Line 11753... |
{
|
{
|
case 'r':
|
case 'r':
|
case 's':
|
case 's':
|
case 'v':
|
case 'v':
|
case 'b':
|
case 'b':
|
INSERT_OPERAND (RS, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, RS, *ip, regno);
|
break;
|
break;
|
case 'd':
|
|
case 'K':
|
case 'K':
|
|
if (mips_opts.micromips)
|
|
INSERT_OPERAND (1, RS, *ip, regno);
|
|
else
|
|
INSERT_OPERAND (0, RD, *ip, regno);
|
|
break;
|
|
|
|
case 'd':
|
case 'g':
|
case 'g':
|
INSERT_OPERAND (RD, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, RD, *ip, regno);
|
break;
|
break;
|
|
|
case 'U':
|
case 'U':
|
INSERT_OPERAND (RD, *ip, regno);
|
gas_assert (!mips_opts.micromips);
|
INSERT_OPERAND (RT, *ip, regno);
|
INSERT_OPERAND (0, RD, *ip, regno);
|
|
INSERT_OPERAND (0, RT, *ip, regno);
|
break;
|
break;
|
|
|
case 'w':
|
case 'w':
|
case 't':
|
case 't':
|
case 'E':
|
case 'E':
|
INSERT_OPERAND (RT, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, RT, *ip, regno);
|
|
break;
|
|
|
|
case 'y':
|
|
gas_assert (mips_opts.micromips);
|
|
INSERT_OPERAND (1, RS3, *ip, regno);
|
break;
|
break;
|
|
|
case 'x':
|
case 'x':
|
/* This case exists because on the r3000 trunc
|
/* This case exists because on the r3000 trunc
|
expands into a macro which requires a gp
|
expands into a macro which requires a gp
|
register. On the r6000 or r4000 it is
|
register. On the r6000 or r4000 it is
|
assembled into a single instruction which
|
assembled into a single instruction which
|
ignores the register. Thus the insn version
|
ignores the register. Thus the insn version
|
is MIPS_ISA2 and uses 'x', and the macro
|
is MIPS_ISA2 and uses 'x', and the macro
|
version is MIPS_ISA1 and uses 't'. */
|
version is MIPS_ISA1 and uses 't'. */
|
break;
|
break;
|
|
|
case 'z':
|
case 'z':
|
/* This case is for the div instruction, which
|
/* This case is for the div instruction, which
|
acts differently if the destination argument
|
acts differently if the destination argument
|
is $0. This only matches $0, and is checked
|
is $0. This only matches $0, and is checked
|
outside the switch. */
|
outside the switch. */
|
Line 9874... |
Line 11809... |
}
|
}
|
switch (*args++)
|
switch (*args++)
|
{
|
{
|
case 'r':
|
case 'r':
|
case 'v':
|
case 'v':
|
INSERT_OPERAND (RS, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
|
continue;
|
continue;
|
|
|
case 'w':
|
case 'w':
|
INSERT_OPERAND (RT, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
|
continue;
|
continue;
|
}
|
}
|
break;
|
break;
|
|
|
case 'O': /* MDMX alignment immediate constant. */
|
case 'O': /* MDMX alignment immediate constant. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
|
as_warn (_("Improper align amount (%ld), using low bits"),
|
as_warn (_("Improper align amount (%ld), using low bits"),
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
INSERT_OPERAND (ALN, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, ALN, *ip, imm_expr.X_add_number);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'Q': /* MDMX vector, element sel, or const. */
|
case 'Q': /* MDMX vector, element sel, or const. */
|
if (s[0] != '$')
|
if (s[0] != '$')
|
{
|
{
|
/* MDMX Immediate. */
|
/* MDMX Immediate. */
|
|
gas_assert (!mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
|
if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
|
as_warn (_("Invalid MDMX Immediate (%ld)"),
|
as_warn (_("Invalid MDMX Immediate (%ld)"),
|
(long) imm_expr.X_add_number);
|
(long) imm_expr.X_add_number);
|
INSERT_OPERAND (FT, *ip, imm_expr.X_add_number);
|
INSERT_OPERAND (0, FT, *ip, imm_expr.X_add_number);
|
if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
|
if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
|
ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
|
ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
|
else
|
else
|
ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
|
ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
Line 9916... |
Line 11854... |
/* Not MDMX Immediate. Fall through. */
|
/* Not MDMX Immediate. Fall through. */
|
case 'X': /* MDMX destination register. */
|
case 'X': /* MDMX destination register. */
|
case 'Y': /* MDMX source register. */
|
case 'Y': /* MDMX source register. */
|
case 'Z': /* MDMX target register. */
|
case 'Z': /* MDMX target register. */
|
is_mdmx = 1;
|
is_mdmx = 1;
|
|
case 'W':
|
|
gas_assert (!mips_opts.micromips);
|
case 'D': /* Floating point destination register. */
|
case 'D': /* Floating point destination register. */
|
case 'S': /* Floating point source register. */
|
case 'S': /* Floating point source register. */
|
case 'T': /* Floating point target register. */
|
case 'T': /* Floating point target register. */
|
case 'R': /* Floating point source register. */
|
case 'R': /* Floating point source register. */
|
case 'V':
|
case 'V':
|
case 'W':
|
|
rtype = RTYPE_FPU;
|
rtype = RTYPE_FPU;
|
if (is_mdmx
|
if (is_mdmx
|
|| (mips_opts.ase_mdmx
|
|| (mips_opts.ase_mdmx
|
&& (ip->insn_mo->pinfo & FP_D)
|
&& (ip->insn_mo->pinfo & FP_D)
|
&& (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY
|
&& (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY
|
Line 9957... |
Line 11896... |
}
|
}
|
switch (c)
|
switch (c)
|
{
|
{
|
case 'D':
|
case 'D':
|
case 'X':
|
case 'X':
|
INSERT_OPERAND (FD, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, FD, *ip, regno);
|
break;
|
break;
|
|
|
case 'V':
|
case 'V':
|
case 'S':
|
case 'S':
|
case 'Y':
|
case 'Y':
|
INSERT_OPERAND (FS, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, FS, *ip, regno);
|
break;
|
break;
|
|
|
case 'Q':
|
case 'Q':
|
/* This is like 'Z', but also needs to fix the MDMX
|
/* This is like 'Z', but also needs to fix the MDMX
|
vector/scalar select bits. Note that the
|
vector/scalar select bits. Note that the
|
scalar immediate case is handled above. */
|
scalar immediate case is handled above. */
|
if (*s == '[')
|
if (*s == '[')
|
Line 10002... |
Line 11943... |
}
|
}
|
/* Fall through. */
|
/* Fall through. */
|
case 'W':
|
case 'W':
|
case 'T':
|
case 'T':
|
case 'Z':
|
case 'Z':
|
INSERT_OPERAND (FT, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, FT, *ip, regno);
|
break;
|
break;
|
|
|
case 'R':
|
case 'R':
|
INSERT_OPERAND (FR, *ip, regno);
|
INSERT_OPERAND (mips_opts.micromips, FR, *ip, regno);
|
break;
|
break;
|
}
|
}
|
lastregno = regno;
|
lastregno = regno;
|
continue;
|
continue;
|
}
|
}
|
|
|
switch (*args++)
|
switch (*args++)
|
{
|
{
|
case 'V':
|
case 'V':
|
INSERT_OPERAND (FS, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
|
continue;
|
continue;
|
|
|
case 'W':
|
case 'W':
|
INSERT_OPERAND (FT, *ip, lastregno);
|
INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
|
continue;
|
continue;
|
}
|
}
|
break;
|
break;
|
|
|
case 'I':
|
case 'I':
|
Line 10237... |
Line 12180... |
if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
|
if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
|
{
|
{
|
int more;
|
int more;
|
offsetT minval, maxval;
|
offsetT minval, maxval;
|
|
|
more = (insn + 1 < &mips_opcodes[NUMOPCODES]
|
more = (insn + 1 < past
|
&& strcmp (insn->name, insn[1].name) == 0);
|
&& strcmp (insn->name, insn[1].name) == 0);
|
|
|
/* If the expression was written as an unsigned number,
|
/* If the expression was written as an unsigned number,
|
only treat it as signed if there are no more
|
only treat it as signed if there are no more
|
alternatives. */
|
alternatives. */
|
Line 10320... |
Line 12263... |
(unsigned long) imm_expr.X_add_number);
|
(unsigned long) imm_expr.X_add_number);
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'a': /* 26-bit address. */
|
case 'a': /* 26-bit address. */
|
|
*offset_reloc = BFD_RELOC_MIPS_JMP;
|
my_getExpression (&offset_expr, s);
|
my_getExpression (&offset_expr, s);
|
s = expr_end;
|
s = expr_end;
|
*offset_reloc = BFD_RELOC_MIPS_JMP;
|
|
continue;
|
continue;
|
|
|
case 'N': /* 3-bit branch condition code. */
|
case 'N': /* 3-bit branch condition code. */
|
case 'M': /* 3-bit compare condition code. */
|
case 'M': /* 3-bit compare condition code. */
|
rtype = RTYPE_CCC;
|
rtype = RTYPE_CCC;
|
if (ip->insn_mo->pinfo & (FP_D | FP_S))
|
if (ip->insn_mo->pinfo & (FP_D | FP_S))
|
rtype |= RTYPE_FCC;
|
rtype |= RTYPE_FCC;
|
if (!reg_lookup (&s, rtype, ®no))
|
if (!reg_lookup (&s, rtype, ®no))
|
|
break;
|
|
if ((strcmp (str + strlen (str) - 3, ".ps") == 0
|
|
|| strcmp (str + strlen (str) - 5, "any2f") == 0
|
|
|| strcmp (str + strlen (str) - 5, "any2t") == 0)
|
|
&& (regno & 1) != 0)
|
|
as_warn (_("Condition code register should be even for %s, "
|
|
"was %d"),
|
|
str, regno);
|
|
if ((strcmp (str + strlen (str) - 5, "any4f") == 0
|
|
|| strcmp (str + strlen (str) - 5, "any4t") == 0)
|
|
&& (regno & 3) != 0)
|
|
as_warn (_("Condition code register should be 0 or 4 for %s, "
|
|
"was %d"),
|
|
str, regno);
|
|
if (*args == 'N')
|
|
INSERT_OPERAND (mips_opts.micromips, BCC, *ip, regno);
|
|
else
|
|
INSERT_OPERAND (mips_opts.micromips, CCC, *ip, regno);
|
|
continue;
|
|
|
|
case 'H':
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
|
|
s += 2;
|
|
if (ISDIGIT (*s))
|
|
{
|
|
c = 0;
|
|
do
|
|
{
|
|
c *= 10;
|
|
c += *s - '0';
|
|
++s;
|
|
}
|
|
while (ISDIGIT (*s));
|
|
}
|
|
else
|
|
c = 8; /* Invalid sel value. */
|
|
|
|
if (c > 7)
|
|
as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
|
|
INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
|
|
continue;
|
|
|
|
case 'e':
|
|
gas_assert (!mips_opts.micromips);
|
|
/* Must be at least one digit. */
|
|
my_getExpression (&imm_expr, s);
|
|
check_absolute_expr (ip, &imm_expr);
|
|
|
|
if ((unsigned long) imm_expr.X_add_number
|
|
> (unsigned long) OP_MASK_VECBYTE)
|
|
{
|
|
as_bad (_("bad byte vector index (%ld)"),
|
|
(long) imm_expr.X_add_number);
|
|
imm_expr.X_add_number = 0;
|
|
}
|
|
|
|
INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case '%':
|
|
gas_assert (!mips_opts.micromips);
|
|
my_getExpression (&imm_expr, s);
|
|
check_absolute_expr (ip, &imm_expr);
|
|
|
|
if ((unsigned long) imm_expr.X_add_number
|
|
> (unsigned long) OP_MASK_VECALIGN)
|
|
{
|
|
as_bad (_("bad byte vector index (%ld)"),
|
|
(long) imm_expr.X_add_number);
|
|
imm_expr.X_add_number = 0;
|
|
}
|
|
|
|
INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'm': /* Opcode extension character. */
|
|
gas_assert (mips_opts.micromips);
|
|
c = *++args;
|
|
switch (c)
|
|
{
|
|
case 'r':
|
|
if (strncmp (s, "$pc", 3) == 0)
|
|
{
|
|
s += 3;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
case 'h':
|
|
case 'i':
|
|
case 'j':
|
|
case 'l':
|
|
case 'm':
|
|
case 'n':
|
|
case 'p':
|
|
case 'q':
|
|
case 's':
|
|
case 't':
|
|
case 'x':
|
|
case 'y':
|
|
case 'z':
|
|
s_reset = s;
|
|
ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
|
|
if (regno == AT && mips_opts.at)
|
|
{
|
|
if (mips_opts.at == ATREG)
|
|
as_warn (_("Used $at without \".set noat\""));
|
|
else
|
|
as_warn (_("Used $%u with \".set at=$%u\""),
|
|
regno, mips_opts.at);
|
|
}
|
|
if (!ok)
|
|
{
|
|
if (c == 'c')
|
|
{
|
|
gas_assert (args[1] == ',');
|
|
regno = lastregno;
|
|
++args;
|
|
}
|
|
else if (c == 't')
|
|
{
|
|
gas_assert (args[1] == ',');
|
|
++args;
|
|
continue; /* Nothing to do. */
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (c == 'j' && !strncmp (ip->insn_mo->name, "jalr", 4))
|
|
{
|
|
if (regno == lastregno)
|
|
{
|
|
insn_error
|
|
= _("Source and destination must be different");
|
|
continue;
|
|
}
|
|
if (regno == 31 && lastregno == 0xffffffff)
|
|
{
|
|
insn_error
|
|
= _("A destination register must be supplied");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (*s == ' ')
|
|
++s;
|
|
if (args[1] != *s)
|
|
{
|
|
if (c == 'e')
|
|
{
|
|
gas_assert (args[1] == ',');
|
|
regno = lastregno;
|
|
s = s_reset;
|
|
++args;
|
|
}
|
|
else if (c == 't')
|
|
{
|
|
gas_assert (args[1] == ',');
|
|
s = s_reset;
|
|
++args;
|
|
continue; /* Nothing to do. */
|
|
}
|
|
}
|
|
|
|
/* Make sure regno is the same as lastregno. */
|
|
if (c == 't' && regno != lastregno)
|
|
break;
|
|
|
|
/* Make sure regno is the same as destregno. */
|
|
if (c == 'x' && regno != destregno)
|
|
break;
|
|
|
|
/* We need to save regno, before regno maps to the
|
|
microMIPS register encoding. */
|
|
lastregno = regno;
|
|
|
|
if (c == 'f')
|
|
destregno = regno;
|
|
|
|
switch (c)
|
|
{
|
|
case 'a':
|
|
if (regno != GP)
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 'b':
|
|
regno = mips32_to_micromips_reg_b_map[regno];
|
|
break;
|
|
|
|
case 'c':
|
|
regno = mips32_to_micromips_reg_c_map[regno];
|
|
break;
|
|
|
|
case 'd':
|
|
regno = mips32_to_micromips_reg_d_map[regno];
|
|
break;
|
|
|
|
case 'e':
|
|
regno = mips32_to_micromips_reg_e_map[regno];
|
|
break;
|
|
|
|
case 'f':
|
|
regno = mips32_to_micromips_reg_f_map[regno];
|
|
break;
|
|
|
|
case 'g':
|
|
regno = mips32_to_micromips_reg_g_map[regno];
|
|
break;
|
|
|
|
case 'h':
|
|
regno = mips32_to_micromips_reg_h_map[regno];
|
|
break;
|
|
|
|
case 'i':
|
|
switch (EXTRACT_OPERAND (1, MI, *ip))
|
|
{
|
|
case 4:
|
|
if (regno == 21)
|
|
regno = 3;
|
|
else if (regno == 22)
|
|
regno = 4;
|
|
else if (regno == 5)
|
|
regno = 5;
|
|
else if (regno == 6)
|
|
regno = 6;
|
|
else if (regno == 7)
|
|
regno = 7;
|
|
else
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 5:
|
|
if (regno == 6)
|
|
regno = 0;
|
|
else if (regno == 7)
|
|
regno = 1;
|
|
else
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 6:
|
|
if (regno == 7)
|
|
regno = 2;
|
|
else
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
default:
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
regno = mips32_to_micromips_reg_l_map[regno];
|
|
break;
|
|
|
|
case 'm':
|
|
regno = mips32_to_micromips_reg_m_map[regno];
|
|
break;
|
|
|
|
case 'n':
|
|
regno = mips32_to_micromips_reg_n_map[regno];
|
|
break;
|
|
|
|
case 'q':
|
|
regno = mips32_to_micromips_reg_q_map[regno];
|
|
break;
|
|
|
|
case 's':
|
|
if (regno != SP)
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 'y':
|
|
if (regno != 31)
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 'z':
|
|
if (regno != ZERO)
|
|
regno = ILLEGAL_REG;
|
|
break;
|
|
|
|
case 'j': /* Do nothing. */
|
|
case 'p':
|
|
case 't':
|
|
case 'x':
|
|
break;
|
|
|
|
default:
|
|
internalError ();
|
|
}
|
|
|
|
if (regno == ILLEGAL_REG)
|
|
break;
|
|
|
|
switch (c)
|
|
{
|
|
case 'b':
|
|
INSERT_OPERAND (1, MB, *ip, regno);
|
|
break;
|
|
|
|
case 'c':
|
|
INSERT_OPERAND (1, MC, *ip, regno);
|
|
break;
|
|
|
|
case 'd':
|
|
INSERT_OPERAND (1, MD, *ip, regno);
|
|
break;
|
|
|
|
case 'e':
|
|
INSERT_OPERAND (1, ME, *ip, regno);
|
|
break;
|
|
|
|
case 'f':
|
|
INSERT_OPERAND (1, MF, *ip, regno);
|
|
break;
|
|
|
|
case 'g':
|
|
INSERT_OPERAND (1, MG, *ip, regno);
|
|
break;
|
|
|
|
case 'h':
|
|
INSERT_OPERAND (1, MH, *ip, regno);
|
|
break;
|
|
|
|
case 'i':
|
|
INSERT_OPERAND (1, MI, *ip, regno);
|
|
break;
|
|
|
|
case 'j':
|
|
INSERT_OPERAND (1, MJ, *ip, regno);
|
|
break;
|
|
|
|
case 'l':
|
|
INSERT_OPERAND (1, ML, *ip, regno);
|
|
break;
|
|
|
|
case 'm':
|
|
INSERT_OPERAND (1, MM, *ip, regno);
|
|
break;
|
|
|
|
case 'n':
|
|
INSERT_OPERAND (1, MN, *ip, regno);
|
|
break;
|
|
|
|
case 'p':
|
|
INSERT_OPERAND (1, MP, *ip, regno);
|
|
break;
|
|
|
|
case 'q':
|
|
INSERT_OPERAND (1, MQ, *ip, regno);
|
|
break;
|
|
|
|
case 'a': /* Do nothing. */
|
|
case 's': /* Do nothing. */
|
|
case 't': /* Do nothing. */
|
|
case 'x': /* Do nothing. */
|
|
case 'y': /* Do nothing. */
|
|
case 'z': /* Do nothing. */
|
|
break;
|
|
|
|
default:
|
|
internalError ();
|
|
}
|
|
continue;
|
|
|
|
case 'A':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMMA, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, -64, 64, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMA, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'B':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| ep.X_op != O_constant)
|
|
break;
|
|
|
|
for (imm = 0; imm < 8; imm++)
|
|
if (micromips_imm_b_map[imm] == ep.X_add_number)
|
|
break;
|
|
if (imm >= 8)
|
|
break;
|
|
|
|
INSERT_OPERAND (1, IMMB, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'C':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| ep.X_op != O_constant)
|
|
break;
|
|
|
|
for (imm = 0; imm < 16; imm++)
|
|
if (micromips_imm_c_map[imm] == ep.X_add_number)
|
|
break;
|
|
if (imm >= 16)
|
|
break;
|
|
|
|
INSERT_OPERAND (1, IMMC, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'D': /* pc relative offset */
|
|
case 'E': /* pc relative offset */
|
|
my_getExpression (&offset_expr, s);
|
|
if (offset_expr.X_op == O_register)
|
|
break;
|
|
|
|
if (!forced_insn_length)
|
|
*offset_reloc = (int) BFD_RELOC_UNUSED + c;
|
|
else if (c == 'D')
|
|
*offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
|
|
else
|
|
*offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'F':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 16, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number;
|
|
INSERT_OPERAND (1, IMMF, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'G':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMMG, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, -1, 15, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number & 15;
|
|
INSERT_OPERAND (1, IMMG, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'H':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMMH, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 16, 1))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 1;
|
|
INSERT_OPERAND (1, IMMH, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'I':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, -1, 127, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number & 127;
|
|
INSERT_OPERAND (1, IMMI, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'J':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMMJ, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 16, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMJ, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'L':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMML, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 16, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number;
|
|
INSERT_OPERAND (1, IMML, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'M':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 1, 9, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number & 7;
|
|
INSERT_OPERAND (1, IMMM, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'N': /* Register list for lwm and swm. */
|
|
{
|
|
/* A comma-separated list of registers and/or
|
|
dash-separated contiguous ranges including
|
|
both ra and a set of one or more registers
|
|
starting at s0 up to s3 which have to be
|
|
consecutive, e.g.:
|
|
|
|
s0, ra
|
|
s0, s1, ra, s2, s3
|
|
s0-s2, ra
|
|
|
|
and any permutations of these. */
|
|
unsigned int reglist;
|
|
int imm;
|
|
|
|
if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
|
|
break;
|
|
|
|
if ((reglist & 0xfff1ffff) != 0x80010000)
|
|
break;
|
|
|
|
reglist = (reglist >> 17) & 7;
|
|
reglist += 1;
|
|
if ((reglist & -reglist) != reglist)
|
|
break;
|
|
|
|
imm = ffs (reglist) - 1;
|
|
INSERT_OPERAND (1, IMMN, *ip, imm);
|
|
}
|
|
continue;
|
|
|
|
case 'O': /* sdbbp 4-bit code. */
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 16, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number;
|
|
INSERT_OPERAND (1, IMMO, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'P':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 32, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMP, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'Q':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMQ, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'U':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
/* Check whether there is only a single bracketed
|
|
expression left. If so, it must be the base register
|
|
and the constant must be zero. */
|
|
if (*s == '(' && strchr (s + 1, '(') == 0)
|
|
{
|
|
INSERT_OPERAND (1, IMMU, *ip, 0);
|
|
continue;
|
|
}
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 32, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMU, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'W':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, 0, 64, 2))
|
|
break;
|
|
|
|
imm = ep.X_add_number >> 2;
|
|
INSERT_OPERAND (1, IMMW, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'X':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| !expr_const_in_range (&ep, -8, 8, 0))
|
|
break;
|
|
|
|
imm = ep.X_add_number;
|
|
INSERT_OPERAND (1, IMMX, *ip, imm);
|
|
}
|
|
s = expr_end;
|
|
continue;
|
|
|
|
case 'Y':
|
|
{
|
|
bfd_reloc_code_real_type r[3];
|
|
expressionS ep;
|
|
int imm;
|
|
|
|
if (my_getSmallExpression (&ep, r, s) > 0
|
|
|| expr_const_in_range (&ep, -2, 2, 2)
|
|
|| !expr_const_in_range (&ep, -258, 258, 2))
|
break;
|
break;
|
if ((strcmp (str + strlen (str) - 3, ".ps") == 0
|
|
|| strcmp (str + strlen (str) - 5, "any2f") == 0
|
imm = ep.X_add_number >> 2;
|
|| strcmp (str + strlen (str) - 5, "any2t") == 0)
|
imm = ((imm >> 1) & ~0xff) | (imm & 0xff);
|
&& (regno & 1) != 0)
|
INSERT_OPERAND (1, IMMY, *ip, imm);
|
as_warn (_("Condition code register should be even for %s, "
|
}
|
"was %d"),
|
s = expr_end;
|
str, regno);
|
|
if ((strcmp (str + strlen (str) - 5, "any4f") == 0
|
|
|| strcmp (str + strlen (str) - 5, "any4t") == 0)
|
|
&& (regno & 3) != 0)
|
|
as_warn (_("Condition code register should be 0 or 4 for %s, "
|
|
"was %d"),
|
|
str, regno);
|
|
if (*args == 'N')
|
|
INSERT_OPERAND (BCC, *ip, regno);
|
|
else
|
|
INSERT_OPERAND (CCC, *ip, regno);
|
|
continue;
|
continue;
|
|
|
case 'H':
|
case 'Z':
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
|
|
s += 2;
|
|
if (ISDIGIT (*s))
|
|
{
|
|
c = 0;
|
|
do
|
|
{
|
{
|
c *= 10;
|
bfd_reloc_code_real_type r[3];
|
c += *s - '0';
|
expressionS ep;
|
++s;
|
|
}
|
|
while (ISDIGIT (*s));
|
|
}
|
|
else
|
|
c = 8; /* Invalid sel value. */
|
|
|
|
if (c > 7)
|
if (my_getSmallExpression (&ep, r, s) > 0
|
as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
|
|| !expr_const_in_range (&ep, 0, 1, 0))
|
ip->insn_opcode |= c;
|
break;
|
|
}
|
|
s = expr_end;
|
continue;
|
continue;
|
|
|
case 'e':
|
default:
|
/* Must be at least one digit. */
|
as_bad (_("Internal error: bad microMIPS opcode "
|
my_getExpression (&imm_expr, s);
|
"(unknown extension operand type `m%c'): %s %s"),
|
check_absolute_expr (ip, &imm_expr);
|
*args, insn->name, insn->args);
|
|
/* Further processing is fruitless. */
|
|
return;
|
|
}
|
|
break;
|
|
|
if ((unsigned long) imm_expr.X_add_number
|
case 'n': /* Register list for 32-bit lwm and swm. */
|
> (unsigned long) OP_MASK_VECBYTE)
|
gas_assert (mips_opts.micromips);
|
{
|
{
|
as_bad (_("bad byte vector index (%ld)"),
|
/* A comma-separated list of registers and/or
|
(long) imm_expr.X_add_number);
|
dash-separated contiguous ranges including
|
imm_expr.X_add_number = 0;
|
at least one of ra and a set of one or more
|
}
|
registers starting at s0 up to s7 and then
|
|
s8 which have to be consecutive, e.g.:
|
|
|
|
ra
|
|
s0
|
|
ra, s0, s1, s2
|
|
s0-s8
|
|
s0-s5, ra
|
|
|
|
and any permutations of these. */
|
|
unsigned int reglist;
|
|
int imm;
|
|
int ra;
|
|
|
INSERT_OPERAND (VECBYTE, *ip, imm_expr.X_add_number);
|
if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
|
|
break;
|
|
|
|
if ((reglist & 0x3f00ffff) != 0)
|
|
break;
|
|
|
|
ra = (reglist >> 27) & 0x10;
|
|
reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
|
|
reglist += 1;
|
|
if ((reglist & -reglist) != reglist)
|
|
break;
|
|
|
|
imm = (ffs (reglist) - 1) | ra;
|
|
INSERT_OPERAND (1, RT, *ip, imm);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
}
|
continue;
|
continue;
|
|
|
case '%':
|
case '|': /* 4-bit trap code. */
|
|
gas_assert (mips_opts.micromips);
|
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
check_absolute_expr (ip, &imm_expr);
|
check_absolute_expr (ip, &imm_expr);
|
|
|
if ((unsigned long) imm_expr.X_add_number
|
if ((unsigned long) imm_expr.X_add_number
|
> (unsigned long) OP_MASK_VECALIGN)
|
> MICROMIPSOP_MASK_TRAP)
|
{
|
as_bad (_("Trap code (%lu) for %s not in 0..15 range"),
|
as_bad (_("bad byte vector index (%ld)"),
|
(unsigned long) imm_expr.X_add_number,
|
(long) imm_expr.X_add_number);
|
ip->insn_mo->name);
|
imm_expr.X_add_number = 0;
|
INSERT_OPERAND (1, TRAP, *ip, imm_expr.X_add_number);
|
}
|
|
|
|
INSERT_OPERAND (VECALIGN, *ip, imm_expr.X_add_number);
|
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
s = expr_end;
|
s = expr_end;
|
continue;
|
continue;
|
|
|
default:
|
default:
|
Line 10415... |
Line 13120... |
internalError ();
|
internalError ();
|
}
|
}
|
break;
|
break;
|
}
|
}
|
/* Args don't match. */
|
/* Args don't match. */
|
if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
|
|
!strcmp (insn->name, insn[1].name))
|
|
{
|
|
++insn;
|
|
s = argsStart;
|
s = argsStart;
|
insn_error = _("Illegal operands");
|
insn_error = _("Illegal operands");
|
|
if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
|
|
{
|
|
++insn;
|
|
continue;
|
|
}
|
|
else if (wrong_delay_slot_insns && need_delay_slot_ok)
|
|
{
|
|
gas_assert (firstinsn);
|
|
need_delay_slot_ok = FALSE;
|
|
past = insn + 1;
|
|
insn = firstinsn;
|
continue;
|
continue;
|
}
|
}
|
if (save_c)
|
|
*(--argsStart) = save_c;
|
|
insn_error = _("Illegal operands");
|
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
|
#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
|
|
|
/* This routine assembles an instruction into its binary format when
|
/* This routine assembles an instruction into its binary format when
|
assembling for the mips16. As a side effect, it sets one of the
|
assembling for the mips16. As a side effect, it sets one of the
|
global variables imm_reloc or offset_reloc to the type of
|
global variables imm_reloc or offset_reloc to the type of relocation
|
relocation to do if one of the operands is an address expression.
|
to do if one of the operands is an address expression. It also sets
|
It also sets mips16_small and mips16_ext if the user explicitly
|
forced_insn_length to the resulting instruction size in bytes if the
|
requested a small or extended instruction. */
|
user explicitly requested a small or extended instruction. */
|
|
|
static void
|
static void
|
mips16_ip (char *str, struct mips_cl_insn *ip)
|
mips16_ip (char *str, struct mips_cl_insn *ip)
|
{
|
{
|
char *s;
|
char *s;
|
Line 10453... |
Line 13162... |
char *s_reset;
|
char *s_reset;
|
size_t i;
|
size_t i;
|
|
|
insn_error = NULL;
|
insn_error = NULL;
|
|
|
mips16_small = FALSE;
|
forced_insn_length = 0;
|
mips16_ext = FALSE;
|
|
|
|
for (s = str; ISLOWER (*s); ++s)
|
for (s = str; ISLOWER (*s); ++s)
|
;
|
;
|
switch (*s)
|
switch (*s)
|
{
|
{
|
Line 10471... |
Line 13179... |
|
|
case '.':
|
case '.':
|
if (s[1] == 't' && s[2] == ' ')
|
if (s[1] == 't' && s[2] == ' ')
|
{
|
{
|
*s = '\0';
|
*s = '\0';
|
mips16_small = TRUE;
|
forced_insn_length = 2;
|
s += 3;
|
s += 3;
|
break;
|
break;
|
}
|
}
|
else if (s[1] == 'e' && s[2] == ' ')
|
else if (s[1] == 'e' && s[2] == ' ')
|
{
|
{
|
*s = '\0';
|
*s = '\0';
|
mips16_ext = TRUE;
|
forced_insn_length = 4;
|
s += 3;
|
s += 3;
|
break;
|
break;
|
}
|
}
|
/* Fall through. */
|
/* Fall through. */
|
default:
|
default:
|
insn_error = _("unknown opcode");
|
insn_error = _("unknown opcode");
|
return;
|
return;
|
}
|
}
|
|
|
if (mips_opts.noautoextend && ! mips16_ext)
|
if (mips_opts.noautoextend && !forced_insn_length)
|
mips16_small = TRUE;
|
forced_insn_length = 2;
|
|
|
if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
|
if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
|
{
|
{
|
insn_error = _("unrecognized opcode");
|
insn_error = _("unrecognized opcode");
|
return;
|
return;
|
Line 10589... |
Line 13297... |
internalError ();
|
internalError ();
|
}
|
}
|
*offset_reloc = BFD_RELOC_UNUSED;
|
*offset_reloc = BFD_RELOC_UNUSED;
|
|
|
mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
|
mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
|
tmp, TRUE, mips16_small,
|
tmp, TRUE, forced_insn_length == 2,
|
mips16_ext, &ip->insn_opcode,
|
forced_insn_length == 4, &ip->insn_opcode,
|
&ip->use_extend, &ip->extend);
|
&ip->use_extend, &ip->extend);
|
imm_expr.X_op = O_absent;
|
imm_expr.X_op = O_absent;
|
*imm_reloc = BFD_RELOC_UNUSED;
|
*imm_reloc = BFD_RELOC_UNUSED;
|
}
|
}
|
|
|
Line 10770... |
Line 13478... |
i = my_getSmallExpression (&imm_expr, imm_reloc, s);
|
i = my_getSmallExpression (&imm_expr, imm_reloc, s);
|
if (i > 0)
|
if (i > 0)
|
{
|
{
|
if (imm_expr.X_op != O_constant)
|
if (imm_expr.X_op != O_constant)
|
{
|
{
|
mips16_ext = TRUE;
|
forced_insn_length = 4;
|
ip->use_extend = TRUE;
|
ip->use_extend = TRUE;
|
ip->extend = 0;
|
ip->extend = 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
Line 11514... |
Line 14222... |
OPTION_NO_MT,
|
OPTION_NO_MT,
|
OPTION_SMARTMIPS,
|
OPTION_SMARTMIPS,
|
OPTION_NO_SMARTMIPS,
|
OPTION_NO_SMARTMIPS,
|
OPTION_DSPR2,
|
OPTION_DSPR2,
|
OPTION_NO_DSPR2,
|
OPTION_NO_DSPR2,
|
|
OPTION_MICROMIPS,
|
|
OPTION_NO_MICROMIPS,
|
|
OPTION_MCU,
|
|
OPTION_NO_MCU,
|
OPTION_COMPAT_ARCH_BASE,
|
OPTION_COMPAT_ARCH_BASE,
|
OPTION_M4650,
|
OPTION_M4650,
|
OPTION_NO_M4650,
|
OPTION_NO_M4650,
|
OPTION_M4010,
|
OPTION_M4010,
|
OPTION_NO_M4010,
|
OPTION_NO_M4010,
|
Line 11606... |
Line 14318... |
{"mno-mt", no_argument, NULL, OPTION_NO_MT},
|
{"mno-mt", no_argument, NULL, OPTION_NO_MT},
|
{"msmartmips", no_argument, NULL, OPTION_SMARTMIPS},
|
{"msmartmips", no_argument, NULL, OPTION_SMARTMIPS},
|
{"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
|
{"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
|
{"mdspr2", no_argument, NULL, OPTION_DSPR2},
|
{"mdspr2", no_argument, NULL, OPTION_DSPR2},
|
{"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
|
{"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
|
|
{"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
|
|
{"mno-micromips", no_argument, NULL, OPTION_NO_MICROMIPS},
|
|
{"mmcu", no_argument, NULL, OPTION_MCU},
|
|
{"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
|
|
|
/* Old-style architecture options. Don't add more of these. */
|
/* Old-style architecture options. Don't add more of these. */
|
{"m4650", no_argument, NULL, OPTION_M4650},
|
{"m4650", no_argument, NULL, OPTION_M4650},
|
{"no-m4650", no_argument, NULL, OPTION_NO_M4650},
|
{"no-m4650", no_argument, NULL, OPTION_NO_M4650},
|
{"m4010", no_argument, NULL, OPTION_M4010},
|
{"m4010", no_argument, NULL, OPTION_M4010},
|
Line 11860... |
Line 14576... |
|
|
case OPTION_NO_MT:
|
case OPTION_NO_MT:
|
mips_opts.ase_mt = 0;
|
mips_opts.ase_mt = 0;
|
break;
|
break;
|
|
|
|
case OPTION_MCU:
|
|
mips_opts.ase_mcu = 1;
|
|
break;
|
|
|
|
case OPTION_NO_MCU:
|
|
mips_opts.ase_mcu = 0;
|
|
break;
|
|
|
|
case OPTION_MICROMIPS:
|
|
if (mips_opts.mips16 == 1)
|
|
{
|
|
as_bad (_("-mmicromips cannot be used with -mips16"));
|
|
return 0;
|
|
}
|
|
mips_opts.micromips = 1;
|
|
mips_no_prev_insn ();
|
|
break;
|
|
|
|
case OPTION_NO_MICROMIPS:
|
|
mips_opts.micromips = 0;
|
|
mips_no_prev_insn ();
|
|
break;
|
|
|
case OPTION_MIPS16:
|
case OPTION_MIPS16:
|
|
if (mips_opts.micromips == 1)
|
|
{
|
|
as_bad (_("-mips16 cannot be used with -micromips"));
|
|
return 0;
|
|
}
|
mips_opts.mips16 = 1;
|
mips_opts.mips16 = 1;
|
mips_no_prev_insn ();
|
mips_no_prev_insn ();
|
break;
|
break;
|
|
|
case OPTION_NO_MIPS16:
|
case OPTION_NO_MIPS16:
|
Line 12298... |
Line 15042... |
|
|
/* If the selected architecture includes support for ASEs, enable
|
/* If the selected architecture includes support for ASEs, enable
|
generation of code for them. */
|
generation of code for them. */
|
if (mips_opts.mips16 == -1)
|
if (mips_opts.mips16 == -1)
|
mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
|
mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
|
|
if (mips_opts.micromips == -1)
|
|
mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
|
if (mips_opts.ase_mips3d == -1)
|
if (mips_opts.ase_mips3d == -1)
|
mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D)
|
mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D)
|
&& file_mips_fp32 == 0) ? 1 : 0;
|
&& file_mips_fp32 == 0) ? 1 : 0;
|
if (mips_opts.ase_mips3d && file_mips_fp32 == 1)
|
if (mips_opts.ase_mips3d && file_mips_fp32 == 1)
|
as_bad (_("-mfp32 used with -mips3d"));
|
as_bad (_("-mfp32 used with -mips3d"));
|
Line 12337... |
Line 15083... |
mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0;
|
mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0;
|
if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE)
|
if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE)
|
as_warn (_("%s ISA does not support MT ASE"),
|
as_warn (_("%s ISA does not support MT ASE"),
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
|
|
|
if (mips_opts.ase_mcu == -1)
|
|
mips_opts.ase_mcu = (arch_info->flags & MIPS_CPU_ASE_MCU) ? 1 : 0;
|
|
if (mips_opts.ase_mcu && !ISA_SUPPORTS_MCU_ASE)
|
|
as_warn (_("%s ISA does not support MCU ASE"),
|
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
|
|
file_mips_isa = mips_opts.isa;
|
file_mips_isa = mips_opts.isa;
|
file_ase_mips3d = mips_opts.ase_mips3d;
|
file_ase_mips3d = mips_opts.ase_mips3d;
|
file_ase_mdmx = mips_opts.ase_mdmx;
|
file_ase_mdmx = mips_opts.ase_mdmx;
|
file_ase_smartmips = mips_opts.ase_smartmips;
|
file_ase_smartmips = mips_opts.ase_smartmips;
|
file_ase_dsp = mips_opts.ase_dsp;
|
file_ase_dsp = mips_opts.ase_dsp;
|
Line 12374... |
Line 15126... |
md_pcrel_from (fixS *fixP)
|
md_pcrel_from (fixS *fixP)
|
{
|
{
|
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
|
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
|
switch (fixP->fx_r_type)
|
switch (fixP->fx_r_type)
|
{
|
{
|
|
case BFD_RELOC_MICROMIPS_7_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_10_PCREL_S1:
|
|
/* Return the address of the delay slot. */
|
|
return addr + 2;
|
|
|
|
case BFD_RELOC_MICROMIPS_16_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_JMP:
|
case BFD_RELOC_16_PCREL_S2:
|
case BFD_RELOC_16_PCREL_S2:
|
case BFD_RELOC_MIPS_JMP:
|
case BFD_RELOC_MIPS_JMP:
|
/* Return the address of the delay slot. */
|
/* Return the address of the delay slot. */
|
return addr + 4;
|
return addr + 4;
|
|
|
default:
|
default:
|
/* We have no relocation type for PC relative MIPS16 instructions. */
|
/* We have no relocation type for PC relative MIPS16 instructions. */
|
if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
|
if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
_("PC relative MIPS16 instruction references a different section"));
|
_("PC relative MIPS16 instruction references a different section"));
|
Line 12537... |
Line 15297... |
mips_force_relocation (fixS *fixp)
|
mips_force_relocation (fixS *fixp)
|
{
|
{
|
if (generic_force_reloc (fixp))
|
if (generic_force_reloc (fixp))
|
return 1;
|
return 1;
|
|
|
|
/* We want to keep BFD_RELOC_MICROMIPS_*_PCREL_S1 relocation,
|
|
so that the linker relaxation can update targets. */
|
|
if (fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
|
|
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
|
|
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
|
|
return 1;
|
|
|
if (HAVE_NEWABI
|
if (HAVE_NEWABI
|
&& S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
|
&& S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
|
&& (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
|
&& (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
|
|| hi16_reloc_p (fixp->fx_r_type)
|
|| hi16_reloc_p (fixp->fx_r_type)
|
|| lo16_reloc_p (fixp->fx_r_type)))
|
|| lo16_reloc_p (fixp->fx_r_type)))
|
Line 12561... |
Line 15328... |
/* We ignore generic BFD relocations we don't know about. */
|
/* We ignore generic BFD relocations we don't know about. */
|
howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
|
howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
|
if (! howto)
|
if (! howto)
|
return;
|
return;
|
|
|
gas_assert (fixP->fx_size == 4
|
gas_assert (fixP->fx_size == 2
|
|
|| fixP->fx_size == 4
|
|| fixP->fx_r_type == BFD_RELOC_16
|
|| fixP->fx_r_type == BFD_RELOC_16
|
|| fixP->fx_r_type == BFD_RELOC_64
|
|| fixP->fx_r_type == BFD_RELOC_64
|
|| fixP->fx_r_type == BFD_RELOC_CTOR
|
|| fixP->fx_r_type == BFD_RELOC_CTOR
|
|| fixP->fx_r_type == BFD_RELOC_MIPS_SUB
|
|| fixP->fx_r_type == BFD_RELOC_MIPS_SUB
|
|
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
|
|| fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|
|| fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
|
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
|
|| fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
|
|| fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
|
|
|
buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
|
buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
|
|
|
gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2);
|
gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
|
|
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
|
|
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
|
|
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1);
|
|
|
/* Don't treat parts of a composite relocation as done. There are two
|
/* Don't treat parts of a composite relocation as done. There are two
|
reasons for this:
|
reasons for this:
|
|
|
(1) The second and third parts will be against 0 (RSS_UNDEF) but
|
(1) The second and third parts will be against 0 (RSS_UNDEF) but
|
Line 12598... |
Line 15370... |
case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
|
case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
|
case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
|
case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
|
case BFD_RELOC_MIPS_TLS_GOTTPREL:
|
case BFD_RELOC_MIPS_TLS_GOTTPREL:
|
case BFD_RELOC_MIPS_TLS_TPREL_HI16:
|
case BFD_RELOC_MIPS_TLS_TPREL_HI16:
|
case BFD_RELOC_MIPS_TLS_TPREL_LO16:
|
case BFD_RELOC_MIPS_TLS_TPREL_LO16:
|
|
case BFD_RELOC_MICROMIPS_TLS_GD:
|
|
case BFD_RELOC_MICROMIPS_TLS_LDM:
|
|
case BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16:
|
|
case BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16:
|
|
case BFD_RELOC_MICROMIPS_TLS_GOTTPREL:
|
|
case BFD_RELOC_MICROMIPS_TLS_TPREL_HI16:
|
|
case BFD_RELOC_MICROMIPS_TLS_TPREL_LO16:
|
S_SET_THREAD_LOCAL (fixP->fx_addsy);
|
S_SET_THREAD_LOCAL (fixP->fx_addsy);
|
/* fall through */
|
/* fall through */
|
|
|
case BFD_RELOC_MIPS_JMP:
|
case BFD_RELOC_MIPS_JMP:
|
case BFD_RELOC_MIPS_SHIFT5:
|
case BFD_RELOC_MIPS_SHIFT5:
|
Line 12634... |
Line 15413... |
case BFD_RELOC_MIPS16_GOT16:
|
case BFD_RELOC_MIPS16_GOT16:
|
case BFD_RELOC_MIPS16_CALL16:
|
case BFD_RELOC_MIPS16_CALL16:
|
case BFD_RELOC_MIPS16_HI16:
|
case BFD_RELOC_MIPS16_HI16:
|
case BFD_RELOC_MIPS16_HI16_S:
|
case BFD_RELOC_MIPS16_HI16_S:
|
case BFD_RELOC_MIPS16_JMP:
|
case BFD_RELOC_MIPS16_JMP:
|
|
case BFD_RELOC_MICROMIPS_JMP:
|
|
case BFD_RELOC_MICROMIPS_GOT_DISP:
|
|
case BFD_RELOC_MICROMIPS_GOT_PAGE:
|
|
case BFD_RELOC_MICROMIPS_GOT_OFST:
|
|
case BFD_RELOC_MICROMIPS_SUB:
|
|
case BFD_RELOC_MICROMIPS_HIGHEST:
|
|
case BFD_RELOC_MICROMIPS_HIGHER:
|
|
case BFD_RELOC_MICROMIPS_SCN_DISP:
|
|
case BFD_RELOC_MICROMIPS_JALR:
|
|
case BFD_RELOC_MICROMIPS_HI16:
|
|
case BFD_RELOC_MICROMIPS_HI16_S:
|
|
case BFD_RELOC_MICROMIPS_GPREL16:
|
|
case BFD_RELOC_MICROMIPS_LITERAL:
|
|
case BFD_RELOC_MICROMIPS_CALL16:
|
|
case BFD_RELOC_MICROMIPS_GOT16:
|
|
case BFD_RELOC_MICROMIPS_GOT_HI16:
|
|
case BFD_RELOC_MICROMIPS_GOT_LO16:
|
|
case BFD_RELOC_MICROMIPS_CALL_HI16:
|
|
case BFD_RELOC_MICROMIPS_CALL_LO16:
|
/* Nothing needed to do. The value comes from the reloc entry. */
|
/* Nothing needed to do. The value comes from the reloc entry. */
|
break;
|
break;
|
|
|
case BFD_RELOC_64:
|
case BFD_RELOC_64:
|
/* This is handled like BFD_RELOC_32, but we output a sign
|
/* This is handled like BFD_RELOC_32, but we output a sign
|
Line 12672... |
Line 15470... |
md_number_to_chars ((char *) buf, *valP, fixP->fx_size);
|
md_number_to_chars ((char *) buf, *valP, fixP->fx_size);
|
break;
|
break;
|
|
|
case BFD_RELOC_LO16:
|
case BFD_RELOC_LO16:
|
case BFD_RELOC_MIPS16_LO16:
|
case BFD_RELOC_MIPS16_LO16:
|
|
case BFD_RELOC_MICROMIPS_LO16:
|
/* FIXME: Now that embedded-PIC is gone, some of this code/comment
|
/* FIXME: Now that embedded-PIC is gone, some of this code/comment
|
may be safe to remove, but if so it's not obvious. */
|
may be safe to remove, but if so it's not obvious. */
|
/* When handling an embedded PIC switch statement, we can wind
|
/* When handling an embedded PIC switch statement, we can wind
|
up deleting a LO16 reloc. See the 'o' case in mips_ip. */
|
up deleting a LO16 reloc. See the 'o' case in mips_ip. */
|
if (fixP->fx_done)
|
if (fixP->fx_done)
|
{
|
{
|
if (*valP + 0x8000 > 0xffff)
|
if (*valP + 0x8000 > 0xffff)
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
_("relocation overflow"));
|
_("relocation overflow"));
|
if (target_big_endian)
|
/* 32-bit microMIPS instructions are divided into two halfwords.
|
|
Relocations always refer to the second halfword, regardless
|
|
of endianness. */
|
|
if (target_big_endian || fixP->fx_r_type == BFD_RELOC_MICROMIPS_LO16)
|
buf += 2;
|
buf += 2;
|
md_number_to_chars ((char *) buf, *valP, 2);
|
md_number_to_chars ((char *) buf, *valP, 2);
|
}
|
}
|
break;
|
break;
|
|
|
Line 12741... |
Line 15543... |
as_bad_where (fixP->fx_file, fixP->fx_line,
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
_("Branch out of range"));
|
_("Branch out of range"));
|
}
|
}
|
break;
|
break;
|
|
|
|
case BFD_RELOC_MICROMIPS_7_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_10_PCREL_S1:
|
|
case BFD_RELOC_MICROMIPS_16_PCREL_S1:
|
|
/* We adjust the offset back to even. */
|
|
if ((*valP & 0x1) != 0)
|
|
--(*valP);
|
|
|
|
if (! fixP->fx_done)
|
|
break;
|
|
|
|
/* Should never visit here, because we keep the relocation. */
|
|
abort ();
|
|
break;
|
|
|
case BFD_RELOC_VTABLE_INHERIT:
|
case BFD_RELOC_VTABLE_INHERIT:
|
fixP->fx_done = 0;
|
fixP->fx_done = 0;
|
if (fixP->fx_addsy
|
if (fixP->fx_addsy
|
&& !S_IS_DEFINED (fixP->fx_addsy)
|
&& !S_IS_DEFINED (fixP->fx_addsy)
|
&& !S_IS_WEAK (fixP->fx_addsy))
|
&& !S_IS_WEAK (fixP->fx_addsy))
|
Line 12786... |
Line 15602... |
|
|
static void
|
static void
|
mips_align (int to, int *fill, symbolS *label)
|
mips_align (int to, int *fill, symbolS *label)
|
{
|
{
|
mips_emit_delays ();
|
mips_emit_delays ();
|
mips_record_mips16_mode ();
|
mips_record_compressed_mode ();
|
if (fill == NULL && subseg_text_p (now_seg))
|
if (fill == NULL && subseg_text_p (now_seg))
|
frag_align_code (to, 0);
|
frag_align_code (to, 0);
|
else
|
else
|
frag_align (to, fill ? *fill : 0, 0);
|
frag_align (to, fill ? *fill : 0, 0);
|
record_alignment (now_seg, to);
|
record_alignment (now_seg, to);
|
Line 13251... |
Line 16067... |
mips_opts.single_float = 1;
|
mips_opts.single_float = 1;
|
else if (strcmp (name, "doublefloat") == 0)
|
else if (strcmp (name, "doublefloat") == 0)
|
mips_opts.single_float = 0;
|
mips_opts.single_float = 0;
|
else if (strcmp (name, "mips16") == 0
|
else if (strcmp (name, "mips16") == 0
|
|| strcmp (name, "MIPS-16") == 0)
|
|| strcmp (name, "MIPS-16") == 0)
|
|
{
|
|
if (mips_opts.micromips == 1)
|
|
as_fatal (_("`mips16' cannot be used with `micromips'"));
|
mips_opts.mips16 = 1;
|
mips_opts.mips16 = 1;
|
|
}
|
else if (strcmp (name, "nomips16") == 0
|
else if (strcmp (name, "nomips16") == 0
|
|| strcmp (name, "noMIPS-16") == 0)
|
|| strcmp (name, "noMIPS-16") == 0)
|
mips_opts.mips16 = 0;
|
mips_opts.mips16 = 0;
|
|
else if (strcmp (name, "micromips") == 0)
|
|
{
|
|
if (mips_opts.mips16 == 1)
|
|
as_fatal (_("`micromips' cannot be used with `mips16'"));
|
|
mips_opts.micromips = 1;
|
|
}
|
|
else if (strcmp (name, "nomicromips") == 0)
|
|
mips_opts.micromips = 0;
|
else if (strcmp (name, "smartmips") == 0)
|
else if (strcmp (name, "smartmips") == 0)
|
{
|
{
|
if (!ISA_SUPPORTS_SMARTMIPS)
|
if (!ISA_SUPPORTS_SMARTMIPS)
|
as_warn (_("%s ISA does not support SmartMIPS ASE"),
|
as_warn (_("%s ISA does not support SmartMIPS ASE"),
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
Line 13307... |
Line 16135... |
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
mips_cpu_info_from_isa (mips_opts.isa)->name);
|
mips_opts.ase_mt = 1;
|
mips_opts.ase_mt = 1;
|
}
|
}
|
else if (strcmp (name, "nomt") == 0)
|
else if (strcmp (name, "nomt") == 0)
|
mips_opts.ase_mt = 0;
|
mips_opts.ase_mt = 0;
|
|
else if (strcmp (name, "mcu") == 0)
|
|
mips_opts.ase_mcu = 1;
|
|
else if (strcmp (name, "nomcu") == 0)
|
|
mips_opts.ase_mcu = 0;
|
else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
|
else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
|
{
|
{
|
int reset = 0;
|
int reset = 0;
|
|
|
/* Permit the user to change the ISA and architecture on the fly.
|
/* Permit the user to change the ISA and architecture on the fly.
|
Line 13595... |
Line 16427... |
macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
|
macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
|
mips_gp_register, 0);
|
mips_gp_register, 0);
|
|
|
if (mips_in_shared || HAVE_64BIT_SYMBOLS)
|
if (mips_in_shared || HAVE_64BIT_SYMBOLS)
|
{
|
{
|
macro_build (&ex_sym, "lui", "t,u", mips_gp_register,
|
macro_build (&ex_sym, "lui", LUI_FMT, mips_gp_register,
|
-1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB,
|
-1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB,
|
BFD_RELOC_HI16_S);
|
BFD_RELOC_HI16_S);
|
|
|
macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register,
|
macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register,
|
mips_gp_register, -1, BFD_RELOC_GPREL16,
|
mips_gp_register, -1, BFD_RELOC_GPREL16,
|
Line 13891... |
Line 16723... |
|
|
demand_empty_rest_of_line ();
|
demand_empty_rest_of_line ();
|
}
|
}
|
|
|
/* Handle the .insn pseudo-op. This marks instruction labels in
|
/* Handle the .insn pseudo-op. This marks instruction labels in
|
mips16 mode. This permits the linker to handle them specially,
|
mips16/micromips mode. This permits the linker to handle them specially,
|
such as generating jalx instructions when needed. We also make
|
such as generating jalx instructions when needed. We also make
|
them odd for the duration of the assembly, in order to generate the
|
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
|
right sort of code. We will make them even in the adjust_symtab
|
routine, while leaving them marked. This is convenient for the
|
routine, while leaving them marked. This is convenient for the
|
debugger and the disassembler. The linker knows to make them odd
|
debugger and the disassembler. The linker knows to make them odd
|
again. */
|
again. */
|
|
|
static void
|
static void
|
s_insn (int ignore ATTRIBUTE_UNUSED)
|
s_insn (int ignore ATTRIBUTE_UNUSED)
|
{
|
{
|
mips16_mark_labels ();
|
mips_mark_labels ();
|
|
|
demand_empty_rest_of_line ();
|
demand_empty_rest_of_line ();
|
}
|
}
|
|
|
/* Handle a .stabn directive. We need these in order to mark a label
|
/* Handle a .stabn directive. We need these in order to mark a label
|
Line 13917... |
Line 16749... |
|
|
static void
|
static void
|
s_mips_stab (int type)
|
s_mips_stab (int type)
|
{
|
{
|
if (type == 'n')
|
if (type == 'n')
|
mips16_mark_labels ();
|
mips_mark_labels ();
|
|
|
s_stab (type);
|
s_stab (type);
|
}
|
}
|
|
|
/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
|
/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
|
Line 14307... |
Line 17139... |
fragp->fr_subtype =
|
fragp->fr_subtype =
|
RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
|
RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
else if (symsec != absolute_section && sec != NULL)
|
else if (symsec != absolute_section && sec != NULL)
|
as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
|
as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
|
|
|
|
if ((val & ((1 << op->shift) - 1)) != 0
|
|
|| val < (mintiny << op->shift)
|
|
|| val > (maxtiny << op->shift))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Compute the length of a branch sequence, and adjust the
|
|
RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
|
|
worst-case length is computed, with UPDATE being used to indicate
|
|
whether an unconditional (-1), branch-likely (+1) or regular (0)
|
|
branch is to be computed. */
|
|
static int
|
|
relaxed_branch_length (fragS *fragp, asection *sec, int update)
|
|
{
|
|
bfd_boolean toofar;
|
|
int length;
|
|
|
|
if (fragp
|
|
&& S_IS_DEFINED (fragp->fr_symbol)
|
|
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
|
|
{
|
|
addressT addr;
|
|
offsetT val;
|
|
|
|
val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
|
|
|
|
addr = fragp->fr_address + fragp->fr_fix + 4;
|
|
|
|
val -= addr;
|
|
|
|
toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
|
|
}
|
|
else if (fragp)
|
|
/* If the symbol is not defined or it's in a different segment,
|
|
assume the user knows what's going on and emit a short
|
|
branch. */
|
|
toofar = FALSE;
|
|
else
|
|
toofar = TRUE;
|
|
|
|
if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
|
|
fragp->fr_subtype
|
|
= RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
|
|
RELAX_BRANCH_UNCOND (fragp->fr_subtype),
|
|
RELAX_BRANCH_LIKELY (fragp->fr_subtype),
|
|
RELAX_BRANCH_LINK (fragp->fr_subtype),
|
|
toofar);
|
|
|
|
length = 4;
|
|
if (toofar)
|
|
{
|
|
if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
|
|
length += 8;
|
|
|
|
if (mips_pic != NO_PIC)
|
|
{
|
|
/* Additional space for PIC loading of target address. */
|
|
length += 8;
|
|
if (mips_opts.isa == ISA_MIPS1)
|
|
/* Additional space for $at-stabilizing nop. */
|
|
length += 4;
|
|
}
|
|
|
|
/* If branch is conditional. */
|
|
if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
|
|
length += 8;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
/* Compute the length of a branch sequence, and adjust the
|
|
RELAX_MICROMIPS_TOOFAR32 bit accordingly. If FRAGP is NULL, the
|
|
worst-case length is computed, with UPDATE being used to indicate
|
|
whether an unconditional (-1), or regular (0) branch is to be
|
|
computed. */
|
|
|
|
static int
|
|
relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
|
|
{
|
|
bfd_boolean toofar;
|
|
int length;
|
|
|
|
if (fragp
|
|
&& S_IS_DEFINED (fragp->fr_symbol)
|
|
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
|
|
{
|
|
addressT addr;
|
|
offsetT val;
|
|
|
|
val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
|
|
/* Ignore the low bit in the target, since it will be set
|
|
for a text label. */
|
|
if ((val & 1) != 0)
|
|
--val;
|
|
|
|
addr = fragp->fr_address + fragp->fr_fix + 4;
|
|
|
|
val -= addr;
|
|
|
|
toofar = val < - (0x8000 << 1) || val >= (0x8000 << 1);
|
|
}
|
|
else if (fragp)
|
|
/* If the symbol is not defined or it's in a different segment,
|
|
assume the user knows what's going on and emit a short
|
|
branch. */
|
|
toofar = FALSE;
|
|
else
|
|
toofar = TRUE;
|
|
|
|
if (fragp && update
|
|
&& toofar != RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
|
|
fragp->fr_subtype = (toofar
|
|
? RELAX_MICROMIPS_MARK_TOOFAR32 (fragp->fr_subtype)
|
|
: RELAX_MICROMIPS_CLEAR_TOOFAR32 (fragp->fr_subtype));
|
|
|
|
length = 4;
|
|
if (toofar)
|
|
{
|
|
bfd_boolean compact_known = fragp != NULL;
|
|
bfd_boolean compact = FALSE;
|
|
bfd_boolean uncond;
|
|
|
|
if (compact_known)
|
|
compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
|
|
if (fragp)
|
|
uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
|
|
else
|
|
uncond = update < 0;
|
|
|
|
/* If label is out of range, we turn branch <br>:
|
|
|
|
<br> label # 4 bytes
|
|
0:
|
|
|
|
into:
|
|
|
|
j label # 4 bytes
|
|
nop # 2 bytes if compact && !PIC
|
|
0:
|
|
*/
|
|
if (mips_pic == NO_PIC && (!compact_known || compact))
|
|
length += 2;
|
|
|
|
/* If assembling PIC code, we further turn:
|
|
|
|
j label # 4 bytes
|
|
|
|
into:
|
|
|
|
lw/ld at, %got(label)(gp) # 4 bytes
|
|
d/addiu at, %lo(label) # 4 bytes
|
|
jr/c at # 2 bytes
|
|
*/
|
|
if (mips_pic != NO_PIC)
|
|
length += 6;
|
|
|
|
/* If branch <br> is conditional, we prepend negated branch <brneg>:
|
|
|
|
<brneg> 0f # 4 bytes
|
|
nop # 2 bytes if !compact
|
|
*/
|
|
if (!uncond)
|
|
length += (compact_known && compact) ? 4 : 6;
|
|
}
|
|
|
if ((val & ((1 << op->shift) - 1)) != 0
|
return length;
|
|| val < (mintiny << op->shift)
|
|
|| val > (maxtiny << op->shift))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
}
|
|
|
/* Compute the length of a branch sequence, and adjust the
|
/* Compute the length of a branch, and adjust the RELAX_MICROMIPS_TOOFAR16
|
RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
|
bit accordingly. */
|
worst-case length is computed, with UPDATE being used to indicate
|
|
whether an unconditional (-1), branch-likely (+1) or regular (0)
|
|
branch is to be computed. */
|
|
static int
|
static int
|
relaxed_branch_length (fragS *fragp, asection *sec, int update)
|
relaxed_micromips_16bit_branch_length (fragS *fragp, asection *sec, int update)
|
{
|
{
|
bfd_boolean toofar;
|
bfd_boolean toofar;
|
int length;
|
|
|
|
if (fragp
|
if (fragp
|
&& S_IS_DEFINED (fragp->fr_symbol)
|
&& S_IS_DEFINED (fragp->fr_symbol)
|
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
|
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
|
{
|
{
|
addressT addr;
|
addressT addr;
|
offsetT val;
|
offsetT val;
|
|
int type;
|
|
|
val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
|
val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
|
|
/* Ignore the low bit in the target, since it will be set
|
|
for a text label. */
|
|
if ((val & 1) != 0)
|
|
--val;
|
|
|
addr = fragp->fr_address + fragp->fr_fix + 4;
|
/* Assume this is a 2-byte branch. */
|
|
addr = fragp->fr_address + fragp->fr_fix + 2;
|
|
|
|
/* We try to avoid the infinite loop by not adding 2 more bytes for
|
|
long branches. */
|
|
|
val -= addr;
|
val -= addr;
|
|
|
toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
|
type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
|
|
if (type == 'D')
|
|
toofar = val < - (0x200 << 1) || val >= (0x200 << 1);
|
|
else if (type == 'E')
|
|
toofar = val < - (0x40 << 1) || val >= (0x40 << 1);
|
|
else
|
|
abort ();
|
}
|
}
|
else if (fragp)
|
|
/* If the symbol is not defined or it's in a different segment,
|
|
assume the user knows what's going on and emit a short
|
|
branch. */
|
|
toofar = FALSE;
|
|
else
|
else
|
|
/* If the symbol is not defined or it's in a different segment,
|
|
we emit a normal 32-bit branch. */
|
toofar = TRUE;
|
toofar = TRUE;
|
|
|
if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
|
if (fragp && update
|
|
&& toofar != RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
|
fragp->fr_subtype
|
fragp->fr_subtype
|
= RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
|
= toofar ? RELAX_MICROMIPS_MARK_TOOFAR16 (fragp->fr_subtype)
|
RELAX_BRANCH_UNCOND (fragp->fr_subtype),
|
: RELAX_MICROMIPS_CLEAR_TOOFAR16 (fragp->fr_subtype);
|
RELAX_BRANCH_LIKELY (fragp->fr_subtype),
|
|
RELAX_BRANCH_LINK (fragp->fr_subtype),
|
|
toofar);
|
|
|
|
length = 4;
|
|
if (toofar)
|
if (toofar)
|
{
|
return 4;
|
if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
|
|
length += 8;
|
|
|
|
if (mips_pic != NO_PIC)
|
|
{
|
|
/* Additional space for PIC loading of target address. */
|
|
length += 8;
|
|
if (mips_opts.isa == ISA_MIPS1)
|
|
/* Additional space for $at-stabilizing nop. */
|
|
length += 4;
|
|
}
|
|
|
|
/* If branch is conditional. */
|
|
if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
|
|
length += 8;
|
|
}
|
|
|
|
return length;
|
return 2;
|
}
|
}
|
|
|
/* Estimate the size of a frag before relaxing. Unless this is the
|
/* Estimate the size of a frag before relaxing. Unless this is the
|
mips16, we are not really relaxing here, and the final size is
|
mips16, we are not really relaxing here, and the final size is
|
encoded in the subtype information. For the mips16, we have to
|
encoded in the subtype information. For the mips16, we have to
|
Line 14406... |
Line 17391... |
if (RELAX_MIPS16_P (fragp->fr_subtype))
|
if (RELAX_MIPS16_P (fragp->fr_subtype))
|
/* We don't want to modify the EXTENDED bit here; it might get us
|
/* We don't want to modify the EXTENDED bit here; it might get us
|
into infinite loops. We change it only in mips_relax_frag(). */
|
into infinite loops. We change it only in mips_relax_frag(). */
|
return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
|
return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
|
|
|
|
if (RELAX_MICROMIPS_P (fragp->fr_subtype))
|
|
{
|
|
int length = 4;
|
|
|
|
if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
|
|
length = relaxed_micromips_16bit_branch_length (fragp, segtype, FALSE);
|
|
if (length == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
|
|
length = relaxed_micromips_32bit_branch_length (fragp, segtype, FALSE);
|
|
fragp->fr_var = length;
|
|
|
|
return length;
|
|
}
|
|
|
if (mips_pic == NO_PIC)
|
if (mips_pic == NO_PIC)
|
change = nopic_need_relax (fragp->fr_symbol, 0);
|
change = nopic_need_relax (fragp->fr_symbol, 0);
|
else if (mips_pic == SVR4_PIC)
|
else if (mips_pic == SVR4_PIC)
|
change = pic_need_relax (fragp->fr_symbol, segtype);
|
change = pic_need_relax (fragp->fr_symbol, segtype);
|
else if (mips_pic == VXWORKS_PIC)
|
else if (mips_pic == VXWORKS_PIC)
|
Line 14463... |
Line 17461... |
/* There is no place to store an in-place offset for JALR relocations.
|
/* There is no place to store an in-place offset for JALR relocations.
|
Likewise an in-range offset of PC-relative relocations may overflow
|
Likewise an in-range offset of PC-relative relocations may overflow
|
the in-place relocatable field if recalculated against the start
|
the in-place relocatable field if recalculated against the start
|
address of the symbol's containing section. */
|
address of the symbol's containing section. */
|
if (HAVE_IN_PLACE_ADDENDS
|
if (HAVE_IN_PLACE_ADDENDS
|
&& (fixp->fx_pcrel || fixp->fx_r_type == BFD_RELOC_MIPS_JALR))
|
&& (fixp->fx_pcrel || jalr_reloc_p (fixp->fx_r_type)))
|
return 0;
|
return 0;
|
|
|
#ifdef OBJ_ELF
|
#ifdef OBJ_ELF
|
/* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
|
/* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
|
to a floating-point stub. The same is true for non-R_MIPS16_26
|
to a floating-point stub. The same is true for non-R_MIPS16_26
|
Line 14493... |
Line 17491... |
4. We cannot reduce a stub's relocations against MIPS16 symbols if
|
4. We cannot reduce a stub's relocations against MIPS16 symbols if
|
that stub might be used.
|
that stub might be used.
|
|
|
There is a further restriction:
|
There is a further restriction:
|
|
|
5. We cannot reduce R_MIPS16_26 relocations against MIPS16 symbols
|
5. We cannot reduce jump relocations (R_MIPS_26, R_MIPS16_26 or
|
on targets with in-place addends; the relocation field cannot
|
R_MICROMIPS_26_S1) against MIPS16 or microMIPS symbols on
|
|
targets with in-place addends; the relocation field cannot
|
encode the low bit.
|
encode the low bit.
|
|
|
For simplicity, we deal with (3)-(5) by not reducing _any_ relocation
|
For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
|
against a MIPS16 symbol.
|
against a MIPS16 symbol. We deal with (5) by by not reducing any
|
|
such relocations on REL targets.
|
|
|
We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
|
We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
|
relocation against some symbol R, no relocation against R may be
|
relocation against some symbol R, no relocation against R may be
|
reduced. (Note that this deals with (2) as well as (1) because
|
reduced. (Note that this deals with (2) as well as (1) because
|
relocations against global symbols will never be reduced on ELF
|
relocations against global symbols will never be reduced on ELF
|
Line 14510... |
Line 17510... |
stub sections, and gives the "all or nothing" per-symbol consistency
|
stub sections, and gives the "all or nothing" per-symbol consistency
|
that we have for MIPS16 symbols. */
|
that we have for MIPS16 symbols. */
|
if (IS_ELF
|
if (IS_ELF
|
&& fixp->fx_subsy == NULL
|
&& fixp->fx_subsy == NULL
|
&& (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
|
&& (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
|
|| *symbol_get_tc (fixp->fx_addsy)))
|
|| *symbol_get_tc (fixp->fx_addsy)
|
|
|| (HAVE_IN_PLACE_ADDENDS
|
|
&& ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
|
|
&& jmp_reloc_p (fixp->fx_r_type))))
|
return 0;
|
return 0;
|
#endif
|
#endif
|
|
|
return 1;
|
return 1;
|
}
|
}
|
Line 14535... |
Line 17538... |
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
|
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
|
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
|
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
|
|
|
if (fixp->fx_pcrel)
|
if (fixp->fx_pcrel)
|
{
|
{
|
gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2);
|
gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
|
|
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
|
|
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
|
|
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1);
|
|
|
/* At this point, fx_addnumber is "symbol offset - pcrel address".
|
/* At this point, fx_addnumber is "symbol offset - pcrel address".
|
Relocations want only the symbol offset. */
|
Relocations want only the symbol offset. */
|
reloc->addend = fixp->fx_addnumber + reloc->address;
|
reloc->addend = fixp->fx_addnumber + reloc->address;
|
if (!IS_ELF)
|
if (!IS_ELF)
|
Line 14589... |
Line 17595... |
fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
|
fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
|
|
|
return fragp->fr_var - old_var;
|
return fragp->fr_var - old_var;
|
}
|
}
|
|
|
|
if (RELAX_MICROMIPS_P (fragp->fr_subtype))
|
|
{
|
|
offsetT old_var = fragp->fr_var;
|
|
offsetT new_var = 4;
|
|
|
|
if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
|
|
new_var = relaxed_micromips_16bit_branch_length (fragp, sec, TRUE);
|
|
if (new_var == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
|
|
new_var = relaxed_micromips_32bit_branch_length (fragp, sec, TRUE);
|
|
fragp->fr_var = new_var;
|
|
|
|
return new_var - old_var;
|
|
}
|
|
|
if (! RELAX_MIPS16_P (fragp->fr_subtype))
|
if (! RELAX_MIPS16_P (fragp->fr_subtype))
|
return 0;
|
return 0;
|
|
|
if (mips16_extended_frag (fragp, NULL, stretch))
|
if (mips16_extended_frag (fragp, NULL, stretch))
|
{
|
{
|
Line 14827... |
Line 17847... |
fragp->fr_fix += fragp->fr_var;
|
fragp->fr_fix += fragp->fr_var;
|
|
|
return;
|
return;
|
}
|
}
|
|
|
|
/* Relax microMIPS branches. */
|
|
if (RELAX_MICROMIPS_P (fragp->fr_subtype))
|
|
{
|
|
bfd_byte *buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
|
|
bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
|
|
bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
|
|
int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
|
|
bfd_boolean short_ds;
|
|
unsigned long insn;
|
|
expressionS exp;
|
|
fixS *fixp;
|
|
|
|
exp.X_op = O_symbol;
|
|
exp.X_add_symbol = fragp->fr_symbol;
|
|
exp.X_add_number = fragp->fr_offset;
|
|
|
|
fragp->fr_fix += fragp->fr_var;
|
|
|
|
/* Handle 16-bit branches that fit or are forced to fit. */
|
|
if (type != 0 && !RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
|
|
{
|
|
/* We generate a fixup instead of applying it right now,
|
|
because if there is linker relaxation, we're going to
|
|
need the relocations. */
|
|
if (type == 'D')
|
|
fixp = fix_new_exp (fragp,
|
|
buf - (bfd_byte *) fragp->fr_literal,
|
|
2, &exp, TRUE,
|
|
BFD_RELOC_MICROMIPS_10_PCREL_S1);
|
|
else if (type == 'E')
|
|
fixp = fix_new_exp (fragp,
|
|
buf - (bfd_byte *) fragp->fr_literal,
|
|
2, &exp, TRUE,
|
|
BFD_RELOC_MICROMIPS_7_PCREL_S1);
|
|
else
|
|
abort ();
|
|
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
/* These relocations can have an addend that won't fit in
|
|
2 octets. */
|
|
fixp->fx_no_overflow = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Handle 32-bit branches that fit or are forced to fit. */
|
|
if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
|
|
|| !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
|
|
{
|
|
/* We generate a fixup instead of applying it right now,
|
|
because if there is linker relaxation, we're going to
|
|
need the relocations. */
|
|
fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
|
|
4, &exp, TRUE, BFD_RELOC_MICROMIPS_16_PCREL_S1);
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
if (type == 0)
|
|
return;
|
|
}
|
|
|
|
/* Relax 16-bit branches to 32-bit branches. */
|
|
if (type != 0)
|
|
{
|
|
if (target_big_endian)
|
|
insn = bfd_getb16 (buf);
|
|
else
|
|
insn = bfd_getl16 (buf);
|
|
|
|
if ((insn & 0xfc00) == 0xcc00) /* b16 */
|
|
insn = 0x94000000; /* beq */
|
|
else if ((insn & 0xdc00) == 0x8c00) /* beqz16/bnez16 */
|
|
{
|
|
unsigned long regno;
|
|
|
|
regno = (insn >> MICROMIPSOP_SH_MD) & MICROMIPSOP_MASK_MD;
|
|
regno = micromips_to_32_reg_d_map [regno];
|
|
insn = ((insn & 0x2000) << 16) | 0x94000000; /* beq/bne */
|
|
insn |= regno << MICROMIPSOP_SH_RS;
|
|
}
|
|
else
|
|
abort ();
|
|
|
|
/* Nothing else to do, just write it out. */
|
|
if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
|
|
|| !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
|
|
{
|
|
md_number_to_chars ((char *) buf, insn >> 16, 2);
|
|
buf += 2;
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
|
|
gas_assert (buf == ((bfd_byte *) fragp->fr_literal
|
|
+ fragp->fr_fix));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned long next;
|
|
|
|
if (target_big_endian)
|
|
{
|
|
insn = bfd_getb16 (buf);
|
|
next = bfd_getb16 (buf + 2);
|
|
}
|
|
else
|
|
{
|
|
insn = bfd_getl16 (buf);
|
|
next = bfd_getl16 (buf + 2);
|
|
}
|
|
insn = (insn << 16) | next;
|
|
}
|
|
|
|
/* Relax 32-bit branches to a sequence of instructions. */
|
|
as_warn_where (fragp->fr_file, fragp->fr_line,
|
|
_("Relaxed out-of-range branch into a jump"));
|
|
|
|
/* Set the short-delay-slot bit. */
|
|
short_ds = al && (insn & 0x02000000) != 0;
|
|
|
|
if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
|
|
{
|
|
symbolS *l;
|
|
|
|
/* Reverse the branch. */
|
|
if ((insn & 0xfc000000) == 0x94000000 /* beq */
|
|
|| (insn & 0xfc000000) == 0xb4000000) /* bne */
|
|
insn ^= 0x20000000;
|
|
else if ((insn & 0xffe00000) == 0x40000000 /* bltz */
|
|
|| (insn & 0xffe00000) == 0x40400000 /* bgez */
|
|
|| (insn & 0xffe00000) == 0x40800000 /* blez */
|
|
|| (insn & 0xffe00000) == 0x40c00000 /* bgtz */
|
|
|| (insn & 0xffe00000) == 0x40a00000 /* bnezc */
|
|
|| (insn & 0xffe00000) == 0x40e00000 /* beqzc */
|
|
|| (insn & 0xffe00000) == 0x40200000 /* bltzal */
|
|
|| (insn & 0xffe00000) == 0x40600000 /* bgezal */
|
|
|| (insn & 0xffe00000) == 0x42200000 /* bltzals */
|
|
|| (insn & 0xffe00000) == 0x42600000) /* bgezals */
|
|
insn ^= 0x00400000;
|
|
else if ((insn & 0xffe30000) == 0x43800000 /* bc1f */
|
|
|| (insn & 0xffe30000) == 0x43a00000 /* bc1t */
|
|
|| (insn & 0xffe30000) == 0x42800000 /* bc2f */
|
|
|| (insn & 0xffe30000) == 0x42a00000) /* bc2t */
|
|
insn ^= 0x00200000;
|
|
else
|
|
abort ();
|
|
|
|
if (al)
|
|
{
|
|
/* Clear the and-link and short-delay-slot bits. */
|
|
gas_assert ((insn & 0xfda00000) == 0x40200000);
|
|
|
|
/* bltzal 0x40200000 bgezal 0x40600000 */
|
|
/* bltzals 0x42200000 bgezals 0x42600000 */
|
|
insn &= ~0x02200000;
|
|
}
|
|
|
|
/* Make a label at the end for use with the branch. */
|
|
l = symbol_new (micromips_label_name (), asec, fragp->fr_fix, fragp);
|
|
micromips_label_inc ();
|
|
#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
|
|
if (IS_ELF)
|
|
S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
|
|
#endif
|
|
|
|
/* Refer to it. */
|
|
fixp = fix_new (fragp, buf - (bfd_byte *) fragp->fr_literal,
|
|
4, l, 0, TRUE, BFD_RELOC_MICROMIPS_16_PCREL_S1);
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
/* Branch over the jump. */
|
|
md_number_to_chars ((char *) buf, insn >> 16, 2);
|
|
buf += 2;
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
|
|
if (!compact)
|
|
{
|
|
/* nop */
|
|
insn = 0x0c00;
|
|
md_number_to_chars ((char *) buf, insn, 2);
|
|
buf += 2;
|
|
}
|
|
}
|
|
|
|
if (mips_pic == NO_PIC)
|
|
{
|
|
unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s */
|
|
|
|
/* j/jal/jals <sym> R_MICROMIPS_26_S1 */
|
|
insn = al ? jal : 0xd4000000;
|
|
|
|
fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
|
|
4, &exp, FALSE, BFD_RELOC_MICROMIPS_JMP);
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
md_number_to_chars ((char *) buf, insn >> 16, 2);
|
|
buf += 2;
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
|
|
if (compact)
|
|
{
|
|
/* nop */
|
|
insn = 0x0c00;
|
|
md_number_to_chars ((char *) buf, insn, 2);
|
|
buf += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned long at = RELAX_MICROMIPS_AT (fragp->fr_subtype);
|
|
unsigned long jalr = short_ds ? 0x45e0 : 0x45c0; /* jalr/s */
|
|
unsigned long jr = compact ? 0x45a0 : 0x4580; /* jr/c */
|
|
|
|
/* lw/ld $at, <sym>($gp) R_MICROMIPS_GOT16 */
|
|
insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
|
|
insn |= at << MICROMIPSOP_SH_RT;
|
|
|
|
if (exp.X_add_number)
|
|
{
|
|
exp.X_add_symbol = make_expr_symbol (&exp);
|
|
exp.X_add_number = 0;
|
|
}
|
|
|
|
fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
|
|
4, &exp, FALSE, BFD_RELOC_MICROMIPS_GOT16);
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
md_number_to_chars ((char *) buf, insn >> 16, 2);
|
|
buf += 2;
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
|
|
/* d/addiu $at, $at, <sym> R_MICROMIPS_LO16 */
|
|
insn = HAVE_64BIT_ADDRESSES ? 0x5c000000 : 0x30000000;
|
|
insn |= at << MICROMIPSOP_SH_RT | at << MICROMIPSOP_SH_RS;
|
|
|
|
fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
|
|
4, &exp, FALSE, BFD_RELOC_MICROMIPS_LO16);
|
|
fixp->fx_file = fragp->fr_file;
|
|
fixp->fx_line = fragp->fr_line;
|
|
|
|
md_number_to_chars ((char *) buf, insn >> 16, 2);
|
|
buf += 2;
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
|
|
/* jr/jrc/jalr/jalrs $at */
|
|
insn = al ? jalr : jr;
|
|
insn |= at << MICROMIPSOP_SH_MJ;
|
|
|
|
md_number_to_chars ((char *) buf, insn & 0xffff, 2);
|
|
buf += 2;
|
|
}
|
|
|
|
gas_assert (buf == (bfd_byte *) fragp->fr_literal + fragp->fr_fix);
|
|
return;
|
|
}
|
|
|
if (RELAX_MIPS16_P (fragp->fr_subtype))
|
if (RELAX_MIPS16_P (fragp->fr_subtype))
|
{
|
{
|
int type;
|
int type;
|
const struct mips16_immed_operand *op;
|
const struct mips16_immed_operand *op;
|
bfd_boolean small, ext;
|
bfd_boolean small, ext;
|
Line 14917... |
Line 18203... |
fragp->fr_fix += 2;
|
fragp->fr_fix += 2;
|
buf += 2;
|
buf += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
|
relax_substateT subtype = fragp->fr_subtype;
|
|
bfd_boolean second_longer = (subtype & RELAX_SECOND_LONGER) != 0;
|
|
bfd_boolean use_second = (subtype & RELAX_USE_SECOND) != 0;
|
int first, second;
|
int first, second;
|
fixS *fixp;
|
fixS *fixp;
|
|
|
first = RELAX_FIRST (fragp->fr_subtype);
|
first = RELAX_FIRST (subtype);
|
second = RELAX_SECOND (fragp->fr_subtype);
|
second = RELAX_SECOND (subtype);
|
fixp = (fixS *) fragp->fr_opcode;
|
fixp = (fixS *) fragp->fr_opcode;
|
|
|
|
/* If the delay slot chosen does not match the size of the instruction,
|
|
then emit a warning. */
|
|
if ((!use_second && (subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0)
|
|
|| (use_second && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0))
|
|
{
|
|
relax_substateT s;
|
|
const char *msg;
|
|
|
|
s = subtype & (RELAX_DELAY_SLOT_16BIT
|
|
| RELAX_DELAY_SLOT_SIZE_FIRST
|
|
| RELAX_DELAY_SLOT_SIZE_SECOND);
|
|
msg = macro_warning (s);
|
|
if (msg != NULL)
|
|
as_warn_where (fragp->fr_file, fragp->fr_line, msg);
|
|
subtype &= ~s;
|
|
}
|
|
|
/* Possibly emit a warning if we've chosen the longer option. */
|
/* Possibly emit a warning if we've chosen the longer option. */
|
if (((fragp->fr_subtype & RELAX_USE_SECOND) != 0)
|
if (use_second == second_longer)
|
== ((fragp->fr_subtype & RELAX_SECOND_LONGER) != 0))
|
|
{
|
{
|
const char *msg = macro_warning (fragp->fr_subtype);
|
relax_substateT s;
|
if (msg != 0)
|
const char *msg;
|
as_warn_where (fragp->fr_file, fragp->fr_line, "%s", msg);
|
|
|
s = (subtype
|
|
& (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT));
|
|
msg = macro_warning (s);
|
|
if (msg != NULL)
|
|
as_warn_where (fragp->fr_file, fragp->fr_line, msg);
|
|
subtype &= ~s;
|
}
|
}
|
|
|
/* Go through all the fixups for the first sequence. Disable them
|
/* Go through all the fixups for the first sequence. Disable them
|
(by marking them as done) if we're going to use the second
|
(by marking them as done) if we're going to use the second
|
sequence instead. */
|
sequence instead. */
|
while (fixp
|
while (fixp
|
&& fixp->fx_frag == fragp
|
&& fixp->fx_frag == fragp
|
&& fixp->fx_where < fragp->fr_fix - second)
|
&& fixp->fx_where < fragp->fr_fix - second)
|
{
|
{
|
if (fragp->fr_subtype & RELAX_USE_SECOND)
|
if (subtype & RELAX_USE_SECOND)
|
fixp->fx_done = 1;
|
fixp->fx_done = 1;
|
fixp = fixp->fx_next;
|
fixp = fixp->fx_next;
|
}
|
}
|
|
|
/* Go through the fixups for the second sequence. Disable them if
|
/* Go through the fixups for the second sequence. Disable them if
|
we're going to use the first sequence, otherwise adjust their
|
we're going to use the first sequence, otherwise adjust their
|
addresses to account for the relaxation. */
|
addresses to account for the relaxation. */
|
while (fixp && fixp->fx_frag == fragp)
|
while (fixp && fixp->fx_frag == fragp)
|
{
|
{
|
if (fragp->fr_subtype & RELAX_USE_SECOND)
|
if (subtype & RELAX_USE_SECOND)
|
fixp->fx_where -= first;
|
fixp->fx_where -= first;
|
else
|
else
|
fixp->fx_done = 1;
|
fixp->fx_done = 1;
|
fixp = fixp->fx_next;
|
fixp = fixp->fx_next;
|
}
|
}
|
|
|
/* Now modify the frag contents. */
|
/* Now modify the frag contents. */
|
if (fragp->fr_subtype & RELAX_USE_SECOND)
|
if (subtype & RELAX_USE_SECOND)
|
{
|
{
|
char *start;
|
char *start;
|
|
|
start = fragp->fr_literal + fragp->fr_fix - first - second;
|
start = fragp->fr_literal + fragp->fr_fix - first - second;
|
memmove (start, start + first, second);
|
memmove (start, start + first, second);
|
Line 14989... |
Line 18300... |
return;
|
return;
|
|
|
syms = bfd_get_outsymbols (stdoutput);
|
syms = bfd_get_outsymbols (stdoutput);
|
count = bfd_get_symcount (stdoutput);
|
count = bfd_get_symcount (stdoutput);
|
for (i = 0; i < count; i++, syms++)
|
for (i = 0; i < count; i++, syms++)
|
{
|
if (ELF_ST_IS_COMPRESSED (elf_symbol (*syms)->internal_elf_sym.st_other)
|
if (ELF_ST_IS_MIPS16 (elf_symbol (*syms)->internal_elf_sym.st_other)
|
|
&& ((*syms)->value & 1) != 0)
|
&& ((*syms)->value & 1) != 0)
|
{
|
{
|
(*syms)->value &= ~1;
|
(*syms)->value &= ~1;
|
/* If the symbol has an odd size, it was probably computed
|
/* If the symbol has an odd size, it was probably computed
|
incorrectly, so adjust that as well. */
|
incorrectly, so adjust that as well. */
|
if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
|
if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
|
++elf_symbol (*syms)->internal_elf_sym.st_size;
|
++elf_symbol (*syms)->internal_elf_sym.st_size;
|
}
|
}
|
}
|
}
|
}
|
|
|
|
#endif
|
#endif
|
|
|
/* This function is called whenever a label is defined, including fake
|
/* This function is called whenever a label is defined, including fake
|
labels instantiated off the dot special symbol. It is used when
|
labels instantiated off the dot special symbol. It is used when
|
Line 15101... |
Line 18410... |
/* Same for DSP R2. */
|
/* Same for DSP R2. */
|
/* We may need to define a new flag for MT ASE, and set this flag when
|
/* We may need to define a new flag for MT ASE, and set this flag when
|
file_ase_mt is true. */
|
file_ase_mt is true. */
|
if (file_ase_mips16)
|
if (file_ase_mips16)
|
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
|
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
|
|
if (file_ase_micromips)
|
|
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
|
#if 0 /* XXX FIXME */
|
#if 0 /* XXX FIXME */
|
if (file_ase_mips3d)
|
if (file_ase_mips3d)
|
elf_elfheader (stdoutput)->e_flags |= ???;
|
elf_elfheader (stdoutput)->e_flags |= ???;
|
#endif
|
#endif
|
if (file_ase_mdmx)
|
if (file_ase_mdmx)
|
Line 15153... |
Line 18464... |
|
|
static procS cur_proc;
|
static procS cur_proc;
|
static procS *cur_proc_ptr;
|
static procS *cur_proc_ptr;
|
static int numprocs;
|
static int numprocs;
|
|
|
/* Implement NOP_OPCODE. We encode a MIPS16 nop as "1" and a normal
|
/* Implement NOP_OPCODE. We encode a MIPS16 nop as "1", a microMIPS nop
|
nop as "0". */
|
as "2", and a normal nop as "0". */
|
|
|
|
#define NOP_OPCODE_MIPS 0
|
|
#define NOP_OPCODE_MIPS16 1
|
|
#define NOP_OPCODE_MICROMIPS 2
|
|
|
char
|
char
|
mips_nop_opcode (void)
|
mips_nop_opcode (void)
|
{
|
{
|
return seg_info (now_seg)->tc_segment_info_data.mips16;
|
if (seg_info (now_seg)->tc_segment_info_data.micromips)
|
|
return NOP_OPCODE_MICROMIPS;
|
|
else if (seg_info (now_seg)->tc_segment_info_data.mips16)
|
|
return NOP_OPCODE_MIPS16;
|
|
else
|
|
return NOP_OPCODE_MIPS;
|
}
|
}
|
|
|
/* Fill in an rs_align_code fragment. This only needs to do something
|
/* Fill in an rs_align_code fragment. Unlike elsewhere we want to use
|
for MIPS16 code, where 0 is not a nop. */
|
32-bit microMIPS NOPs here (if applicable). */
|
|
|
void
|
void
|
mips_handle_align (fragS *fragp)
|
mips_handle_align (fragS *fragp)
|
{
|
{
|
|
char nop_opcode;
|
char *p;
|
char *p;
|
int bytes, size, excess;
|
int bytes, size, excess;
|
valueT opcode;
|
valueT opcode;
|
|
|
if (fragp->fr_type != rs_align_code)
|
if (fragp->fr_type != rs_align_code)
|
return;
|
return;
|
|
|
p = fragp->fr_literal + fragp->fr_fix;
|
p = fragp->fr_literal + fragp->fr_fix;
|
if (*p)
|
nop_opcode = *p;
|
|
switch (nop_opcode)
|
{
|
{
|
|
case NOP_OPCODE_MICROMIPS:
|
|
opcode = micromips_nop32_insn.insn_opcode;
|
|
size = 4;
|
|
break;
|
|
case NOP_OPCODE_MIPS16:
|
opcode = mips16_nop_insn.insn_opcode;
|
opcode = mips16_nop_insn.insn_opcode;
|
size = 2;
|
size = 2;
|
}
|
break;
|
else
|
case NOP_OPCODE_MIPS:
|
{
|
default:
|
opcode = nop_insn.insn_opcode;
|
opcode = nop_insn.insn_opcode;
|
size = 4;
|
size = 4;
|
|
break;
|
}
|
}
|
|
|
bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
|
bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
|
excess = bytes % size;
|
excess = bytes % size;
|
if (excess != 0)
|
|
{
|
/* Handle the leading part if we're not inserting a whole number of
|
/* If we're not inserting a whole number of instructions,
|
instructions, and make it the end of the fixed part of the frag.
|
pad the end of the fixed part of the frag with zeros. */
|
Try to fit in a short microMIPS NOP if applicable and possible,
|
memset (p, 0, excess);
|
and use zeroes otherwise. */
|
p += excess;
|
gas_assert (excess < 4);
|
fragp->fr_fix += excess;
|
fragp->fr_fix += excess;
|
|
switch (excess)
|
|
{
|
|
case 3:
|
|
*p++ = '\0';
|
|
/* Fall through. */
|
|
case 2:
|
|
if (nop_opcode == NOP_OPCODE_MICROMIPS)
|
|
{
|
|
md_number_to_chars (p, micromips_nop16_insn.insn_opcode, 2);
|
|
p += 2;
|
|
break;
|
|
}
|
|
*p++ = '\0';
|
|
/* Fall through. */
|
|
case 1:
|
|
*p++ = '\0';
|
|
/* Fall through. */
|
|
case 0:
|
|
break;
|
}
|
}
|
|
|
md_number_to_chars (p, opcode, size);
|
md_number_to_chars (p, opcode, size);
|
fragp->fr_var = size;
|
fragp->fr_var = size;
|
}
|
}
|
Line 15613... |
Line 18960... |
{ "4kem", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "4kem", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "4kep", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "4kep", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "4ksd", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "4ksd", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "m4kp", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "m4kp", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
|
{ "m14k", MIPS_CPU_ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
|
|
{ "m14kc", MIPS_CPU_ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf2_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf2_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf1_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
{ "24kf1_1", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
|
/* Deprecated forms of the above. */
|
/* Deprecated forms of the above. */
|
Line 15898... |
Line 19247... |
|
|
fprintf (stream, _("\
|
fprintf (stream, _("\
|
-mips16 generate mips16 instructions\n\
|
-mips16 generate mips16 instructions\n\
|
-no-mips16 do not generate mips16 instructions\n"));
|
-no-mips16 do not generate mips16 instructions\n"));
|
fprintf (stream, _("\
|
fprintf (stream, _("\
|
|
-mmicromips generate microMIPS instructions\n\
|
|
-mno-micromips do not generate microMIPS instructions\n"));
|
|
fprintf (stream, _("\
|
-msmartmips generate smartmips instructions\n\
|
-msmartmips generate smartmips instructions\n\
|
-mno-smartmips do not generate smartmips instructions\n"));
|
-mno-smartmips do not generate smartmips instructions\n"));
|
fprintf (stream, _("\
|
fprintf (stream, _("\
|
-mdsp generate DSP instructions\n\
|
-mdsp generate DSP instructions\n\
|
-mno-dsp do not generate DSP instructions\n"));
|
-mno-dsp do not generate DSP instructions\n"));
|
Line 15910... |
Line 19262... |
-mno-dspr2 do not generate DSP R2 instructions\n"));
|
-mno-dspr2 do not generate DSP R2 instructions\n"));
|
fprintf (stream, _("\
|
fprintf (stream, _("\
|
-mmt generate MT instructions\n\
|
-mmt generate MT instructions\n\
|
-mno-mt do not generate MT instructions\n"));
|
-mno-mt do not generate MT instructions\n"));
|
fprintf (stream, _("\
|
fprintf (stream, _("\
|
|
-mmcu generate MCU instructions\n\
|
|
-mno-mcu do not generate MCU instructions\n"));
|
|
fprintf (stream, _("\
|
-mfix-loongson2f-jump work around Loongson2F JUMP instructions\n\
|
-mfix-loongson2f-jump work around Loongson2F JUMP instructions\n\
|
-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
|
-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
|
-mfix-vr4120 work around certain VR4120 errata\n\
|
-mfix-vr4120 work around certain VR4120 errata\n\
|
-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
|
-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
|
-mfix-24k insert a nop after ERET and DERET instructions\n\
|
-mfix-24k insert a nop after ERET and DERET instructions\n\
|