|| (ISA) == ISA_MIPS4 \
|| (ISA) == ISA_MIPS5 \
|| (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
)
/* Return true if ISA supports 64-bit right rotate (dror et al.)
instructions. */
#define ISA_HAS_DROR(ISA) ( \
- 0 \
+ (ISA) == ISA_MIPS64R2 \
)
/* Return true if ISA supports 32-bit right rotate (ror et al.)
instructions. */
#define ISA_HAS_ROR(ISA) ( \
(ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS64R2 \
)
#define HAVE_32BIT_GPRS \
&& mips_pic != EMBEDDED_PIC))
#define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
-#define HAVE_64BIT_ADDRESS_CONSTANTS (HAVE_64BIT_ADDRESSES \
- || HAVE_64BIT_GPRS)
/* Addresses are loaded in different ways, depending on the address size
in use. The n32 ABI Documentation also mandates the use of additions
mips_ip. */
static expressionS imm_expr;
+static expressionS imm2_expr;
static expressionS offset_expr;
/* Relocs associated with imm_expr and offset_expr. */
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
imm_expr.X_op = O_absent;
+ imm2_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
imm_reloc[0] = BFD_RELOC_UNUSED;
imm_reloc[1] = BFD_RELOC_UNUSED;
case ')':
continue;
+ case '+':
+ switch (*fmt++)
+ {
+ case 'A':
+ case 'E':
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_SHAMT) << OP_SH_SHAMT;
+ continue;
+
+ case 'B':
+ case 'F':
+ /* Note that in the macro case, these arguments are already
+ in MSB form. (When handling the instruction in the
+ non-macro case, these arguments are sizes from which
+ MSB values must be calculated.) */
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_INSMSB) << OP_SH_INSMSB;
+ continue;
+
+ case 'C':
+ case 'G':
+ case 'H':
+ /* Note that in the macro case, these arguments are already
+ in MSBD form. (When handling the instruction in the
+ non-macro case, these arguments are sizes from which
+ MSBD values must be calculated.) */
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+ continue;
+
+ default:
+ internalError ();
+ }
+ continue;
+
case 't':
case 'w':
case 'E':
using AT if necessary. */
static void
macro_build_ldst_constoffset (char *place, int *counter, expressionS *ep,
- const char *op, int treg, int breg)
+ const char *op, int treg, int breg, int dbl)
{
assert (ep->X_op == O_constant);
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("too large constant specified"));
+
+ ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+ }
+
/* Right now, this routine can only handle signed 32-bit contants. */
- if (! IS_SEXT_32BIT_NUM(ep->X_add_number))
+ if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
as_warn (_("operand overflow"));
if (IS_SEXT_16BIT_NUM(ep->X_add_number))
if (ep->X_op != O_big)
{
assert (ep->X_op == O_constant);
- if (ep->X_add_number < 0x8000
- && (ep->X_add_number >= 0
- || (ep->X_add_number >= -0x8000
- && (! dbl
- || ! ep->X_unsigned
- || sizeof (ep->X_add_number) > 4))))
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("too large constant specified"));
+
+ ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+ }
+
+ if (IS_SEXT_16BIT_NUM (ep->X_add_number))
{
/* We can handle 16 bit signed values with an addiu to
$zero. No need to ever use daddiu here, since $zero and
BFD_RELOC_LO16);
return;
}
- else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)
- && (! dbl
- || ! ep->X_unsigned
- || sizeof (ep->X_add_number) > 4
- || (ep->X_add_number & 0x80000000) == 0))
- || ((HAVE_32BIT_GPRS || ! dbl)
- && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
- || (HAVE_32BIT_GPRS
- && ! dbl
- && ((ep->X_add_number &~ (offsetT) 0xffffffff)
- == ~ (offsetT) 0xffffffff)))
+ else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
{
/* 32 bit values require an lui. */
macro_build (NULL, counter, ep, "lui", "t,u", reg, BFD_RELOC_HI16);
"s,t,p", AT, 0);
break;
+ case M_DEXT:
+ {
+ unsigned long pos;
+ unsigned long size;
+
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = (unsigned long) imm_expr.X_add_number;
+ size = (unsigned long) imm2_expr.X_add_number;
+ }
+
+ if (pos > 63)
+ {
+ as_bad (_("Improper position (%lu)"), pos);
+ pos = 1;
+ }
+ if (size == 0 || size > 64
+ || (pos + size - 1) > 63)
+ {
+ as_bad (_("Improper extract size (%lu, position %lu)"),
+ size, pos);
+ size = 1;
+ }
+
+ if (size <= 32 && pos < 32)
+ {
+ s = "dext";
+ fmt = "t,r,+A,+C";
+ }
+ else if (size <= 32)
+ {
+ s = "dextu";
+ fmt = "t,r,+E,+H";
+ }
+ else
+ {
+ s = "dextm";
+ fmt = "t,r,+A,+G";
+ }
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
+ fmt, treg, sreg, pos, size - 1);
+ }
+ return;
+
+ case M_DINS:
+ {
+ unsigned long pos;
+ unsigned long size;
+
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = (unsigned long) imm_expr.X_add_number;
+ size = (unsigned long) imm2_expr.X_add_number;
+ }
+
+ if (pos > 63)
+ {
+ as_bad (_("Improper position (%lu)"), pos);
+ pos = 1;
+ }
+ if (size == 0 || size > 64
+ || (pos + size - 1) > 63)
+ {
+ as_bad (_("Improper insert size (%lu, position %lu)"),
+ size, pos);
+ size = 1;
+ }
+
+ if (pos < 32 && (pos + size - 1) < 32)
+ {
+ s = "dins";
+ fmt = "t,r,+A,+B";
+ }
+ else if (pos >= 32)
+ {
+ s = "dinsu";
+ fmt = "t,r,+E,+F";
+ }
+ else
+ {
+ s = "dinsm";
+ fmt = "t,r,+A,+F";
+ }
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
+ fmt, treg, sreg, pos, pos + size - 1);
+ }
+ return;
+
case M_DDIV_3:
dbl = 1;
case M_DIV_3:
RELAX_ENCODE (8, 4, 0, 0, 0, 0),
offset_expr.X_add_symbol, 0, NULL);
}
- else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
{
int dreg;
mips_opts.warn_about_macros),
offset_expr.X_add_symbol, 0, NULL);
}
- else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
{
int dreg;
macro_build_ldst_constoffset (NULL, &icnt, &expr1,
ADDRESS_LOAD_INSN,
mips_gp_register,
- mips_frame_reg);
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
}
}
}
macro_build_ldst_constoffset (NULL, &icnt, &expr1,
ADDRESS_LOAD_INSN,
mips_gp_register,
- mips_frame_reg);
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
}
}
}
else
fmt = "t,o(b)";
+ /* Sign-extending 32-bit constants makes their handling easier.
+ The HAVE_64BIT_GPRS... part is due to the linux kernel hack
+ described below. */
+ if ((! HAVE_64BIT_ADDRESSES
+ && (! HAVE_64BIT_GPRS && offset_expr.X_op == O_constant))
+ && (offset_expr.X_op == O_constant)
+ && ! ((offset_expr.X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (offset_expr.X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("too large constant specified"));
+
+ offset_expr.X_add_number = (((offset_expr.X_add_number & 0xffffffff)
+ ^ 0x80000000) - 0x80000000);
+ }
+
/* For embedded PIC, we allow loads where the offset is calculated
by subtracting a symbol in the current segment from an unknown
symbol, relative to a base register, e.g.:
end up converting the binary to ELF32 for a number of
platforms whose boot loaders don't support ELF64
binaries. */
- if ((offset_expr.X_op != O_constant && HAVE_64BIT_ADDRESSES)
- || (offset_expr.X_op == O_constant
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)
- && HAVE_64BIT_ADDRESS_CONSTANTS))
+ if ((HAVE_64BIT_ADDRESSES
+ && ! (offset_expr.X_op == O_constant
+ && IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
+ || (HAVE_64BIT_GPRS
+ && offset_expr.X_op == O_constant
+ && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
{
p = NULL;
return;
}
- else if (offset_expr.X_op == O_constant
- && !HAVE_64BIT_ADDRESS_CONSTANTS
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+
+ if (offset_expr.X_op == O_constant
+ && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000))
as_bad (_("load/store address overflow (max 32 bits)"));
if (breg == 0)
case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD);
USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
+ case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
+ case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
+ case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
+ case 'I': break;
default:
as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
c, opc->name, opc->args);
case 'A': /* ins/ext position, becomes LSB. */
limlo = 0;
limhi = 31;
+ goto do_lsb;
+ case 'E':
+ limlo = 32;
+ limhi = 63;
+ goto do_lsb;
+do_lsb:
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
if ((unsigned long) imm_expr.X_add_number < limlo
case 'B': /* ins size, becomes MSB. */
limlo = 1;
limhi = 32;
+ goto do_msb;
+ case 'F':
+ limlo = 33;
+ limhi = 64;
+ goto do_msb;
+do_msb:
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
/* Check for negative input so that small negative numbers
case 'C': /* ext size, becomes MSBD. */
limlo = 1;
limhi = 32;
+ goto do_msbd;
+ case 'G':
+ limlo = 33;
+ limhi = 64;
+ goto do_msbd;
+ case 'H':
+ limlo = 33;
+ limhi = 64;
+ goto do_msbd;
+do_msbd:
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
/* Check for negative input so that small negative numbers
/* +D is for disassembly only; never match. */
break;
+ case 'I':
+ /* "+I" is like "I", except that imm2_expr is used. */
+ my_getExpression (&imm2_expr, s);
+ if (imm2_expr.X_op != O_big
+ && imm2_expr.X_op != O_constant)
+ insn_error = _("absolute expression required");
+ s = expr_end;
+ continue;
+
default:
as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
*args, insn->name, insn->args);
}
while (ISDIGIT (*s));
if (regno > 7)
- as_bad (_("invalid condition code register $fcc%d"), regno);
+ as_bad (_("Invalid condition code register $fcc%d"), regno);
+ 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')
ip->insn_opcode |= regno << OP_SH_BCC;
else
imm_reloc[0] = BFD_RELOC_UNUSED;
imm_reloc[1] = BFD_RELOC_UNUSED;
imm_reloc[2] = BFD_RELOC_UNUSED;
+ imm2_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
offset_reloc[0] = BFD_RELOC_UNUSED;
offset_reloc[1] = BFD_RELOC_UNUSED;
{"mips64", no_argument, NULL, OPTION_MIPS64},
#define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9)
{"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+#define OPTION_MIPS64R2 (OPTION_ARCH_BASE + 10)
+ {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
/* Options which specify Application Specific Extensions (ASEs). */
-#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 10)
+#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 11)
#define OPTION_MIPS16 (OPTION_ASE_BASE + 0)
{"mips16", no_argument, NULL, OPTION_MIPS16},
#define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1)
file_mips_isa = ISA_MIPS32R2;
break;
+ case OPTION_MIPS64R2:
+ file_mips_isa = ISA_MIPS64R2;
+ break;
+
case OPTION_MIPS64:
file_mips_isa = ISA_MIPS64;
break;
mips_opts.isa = ISA_MIPS32R2;
else if (strcmp (name, "mips64") == 0)
mips_opts.isa = ISA_MIPS64;
+ else if (strcmp (name, "mips64r2") == 0)
+ mips_opts.isa = ISA_MIPS64R2;
else if (strcmp (name, "arch=default") == 0)
{
reset = 1;
case ISA_MIPS4:
case ISA_MIPS5:
case ISA_MIPS64:
+ case ISA_MIPS64R2:
mips_opts.gp32 = 0;
mips_opts.fp32 = 0;
break;
ex.X_add_number = mips_cprestore_offset;
macro_build_ldst_constoffset (NULL, &icnt, &ex, ADDRESS_STORE_INSN,
- mips_gp_register, SP);
+ mips_gp_register, SP, HAVE_64BIT_ADDRESSES);
demand_empty_rest_of_line ();
}
/* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
- was given in the preceeding .gpsetup, it results in:
+ was given in the preceeding .cpsetup, it results in:
ld $gp, offset($sp)
If a register $reg2 was given there, it results in:
- daddiu $gp, $gp, $reg2
+ daddu $gp, $reg2, $0
*/
static void
s_cpreturn (int ignore ATTRIBUTE_UNUSED)
{ "mips32", 1, ISA_MIPS32, CPU_MIPS32 },
{ "mips32r2", 1, ISA_MIPS32R2, CPU_MIPS32R2 },
{ "mips64", 1, ISA_MIPS64, CPU_MIPS64 },
+ { "mips64r2", 1, ISA_MIPS64R2, CPU_MIPS64R2 },
/* MIPS I */
{ "r3000", 0, ISA_MIPS1, CPU_R3000 },
-mips32 generate MIPS32 ISA instructions\n\
-mips32r2 generate MIPS32 release 2 ISA instructions\n\
-mips64 generate MIPS64 ISA instructions\n\
+-mips64r2 generate MIPS64 release 2 ISA instructions\n\
-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n"));
first = 1;