[PATCH v2 3/9] Add MIPS Allegrex VFPU prefix instructions and support for decorated registers.
David Guillen Fandos
david@davidgf.net
Tue Jan 20 23:37:19 GMT 2026
Prefix instructions push a prefix operation onto the prefix coprocessor
registers. These registers are used when reading/writing registers at
a later instruction. For this reason, the assembler also supports a
shorter syntax where registers are decorated and the required prefix
instructions are emitted before the actual instruction.
Signed-off-by: David Guillen Fandos <david@davidgf.net>
---
gas/config/tc-mips.c | 249 +++++++++++++++++-
gas/testsuite/gas/mips/allegrex-vfpu-errors.d | 3 +
gas/testsuite/gas/mips/allegrex-vfpu-errors.l | 10 +
gas/testsuite/gas/mips/allegrex-vfpu-errors.s | 16 ++
gas/testsuite/gas/mips/allegrex-vfpu.d | 16 ++
gas/testsuite/gas/mips/allegrex-vfpu.s | 11 +
.../gas/mips/allegrex@isa-override-1.d | 2 +-
gas/testsuite/gas/mips/mips.exp | 1 +
include/opcode/mips.h | 5 +
opcodes/mips-dis.c | 55 ++++
opcodes/mips-formats.h | 16 +-
opcodes/mips-opc.c | 13 +
12 files changed, 390 insertions(+), 7 deletions(-)
create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.d
create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.l
create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.s
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index f46d81ecfa2..eda2b641b80 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -675,6 +675,8 @@ static int g_switch_seen = 0;
#define N_RMASK 0xc4
#define N_VFP 0xd4
+#define PFX_DST 0x80
+
/* If we can determine in advance that GP optimization won't be
possible, we can skip the relaxation stuff that tries to produce
GP-relative references. This makes delay slot optimization work
@@ -3426,6 +3428,106 @@ mips_vfpu_parse_register (char **sptr, unsigned int *regval,
return true;
}
+/* Parses one VFPU prefix channel, which can be either source or destination.
+ Returns NULL if the parsed data cannot be a valid prefix. */
+static char *
+mips_vfpu_parse_prefix_channel (char *s, unsigned char *chan)
+{
+ static const struct
+ {
+ const char *token;
+ unsigned char value;
+ } fixed_tokens[] = {
+ { "m", PFX_DST | 4 }, { "M", PFX_DST | 4 },
+ { "0:1", PFX_DST | 1 }, { "[0:1]", PFX_DST | 1 },
+ { "-1:1", PFX_DST | 3 }, { "[-1:1]", PFX_DST | 3 },
+ { "1/6", 0x8 | 7 }, { "-1/6", 0x10 | 0x8 | 7 },
+ { "1/4", 0x8 | 6 }, { "-1/4", 0x10 | 0x8 | 6 },
+ { "1/3", 0x8 | 5 }, { "-1/3", 0x10 | 0x8 | 5 },
+ { "3", 0x8 | 4 }, { "-3", 0x10 | 0x8 | 4 },
+ { "1/2", 0x8 | 3 }, { "-1/2", 0x10 | 0x8 | 3 },
+ { "2", 0x8 | 2 }, { "-2", 0x10 | 0x8 | 2 },
+ { "1", 0x8 | 1 }, { "-1", 0x10 | 0x8 | 1 },
+ { "0", 0x8 | 0 }, { "-0", 0x10 | 0x8 | 0 },
+ };
+
+ SKIP_SPACE_TABS (s);
+
+ if (!(*s) || *s == ',' || *s == ']')
+ return s;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(fixed_tokens); i++) {
+ size_t tokenl = strlen(fixed_tokens[i].token);
+ if (!strncmp(s, fixed_tokens[i].token, tokenl)) {
+ *chan = fixed_tokens[i].value;
+ return &s[tokenl];
+ }
+ }
+
+ *chan = 0;
+ if (*s == '-')
+ {
+ *chan |= 0x10;
+ s++;
+ SKIP_SPACE_TABS (s);
+ }
+
+ if (*s == '|')
+ {
+ *chan |= 0x4;
+ s++;
+ SKIP_SPACE_TABS (s);
+ }
+
+ switch (*s++) {
+ case 'x': case 'X': *chan |= 0; break;
+ case 'y': case 'Y': *chan |= 1; break;
+ case 'z': case 'Z': *chan |= 2; break;
+ case 'w': case 'W': *chan |= 3; break;
+ default:
+ set_insn_error (0, _("invalid VFPU prefix"));
+ return NULL;
+ };
+ SKIP_SPACE_TABS (s);
+
+ if (*chan & 0x4 && *s++ != '|')
+ {
+ set_insn_error (0, _("unmatched '|' in VFPU prefix"));
+ return NULL;
+ }
+ return s;
+}
+
+/* Try to parse a VFPU prefix expression only validating its syntax. */
+static char *
+mips_vfpu_parse_prefix (char *s, unsigned char *channels,
+ unsigned int *numchs)
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ channels[i] = 0xff; /* Unespecified channel expression. */
+
+ for (i = 0; i < 4; i++)
+ {
+ s = mips_vfpu_parse_prefix_channel (s, &channels[i]);
+ if (!s)
+ break;
+
+ SKIP_SPACE_TABS (s);
+ *numchs = i + 1;
+
+ if (!(*s) || *s == ']')
+ break;
+
+ if (*s++ != ',')
+ {
+ set_insn_error (0, _("invalid VFPU prefix, expecting ',' or ']'"));
+ return NULL;
+ }
+ }
+ return s;
+}
+
/* Token types for parsed operand lists. */
enum mips_operand_token_type {
/* A plain register, e.g. $f2. */
@@ -3446,6 +3548,9 @@ enum mips_operand_token_type {
/* A VFPU register, eg. S000.s. */
OT_REG_VFPU,
+ /* A VFPU prefix expression for a register or a prefix instruction. */
+ OT_PFX_VFPU,
+
/* A (possibly relocated) expression. */
OT_INTEGER,
@@ -3491,6 +3596,12 @@ struct mips_operand_token
enum mips_vfpu_reg_shape shape;
} reg_vfpu;
+ /* The prefix value (from 1 to 4 channels) to apply to a register. */
+ struct {
+ unsigned char prefixes[4];
+ unsigned int num_chs;
+ } pfx_vfpu;
+
/* The value of an OT_INTEGER. The value is represented as an
expression and the relocation operators that were applied to
that expression. The reloc entries are BFD_RELOC_UNUSED if no
@@ -3606,6 +3717,17 @@ mips_parse_argument_token (char *s, char float_format,
if (end)
return end;
+ /* Handle VFPU specially encoded immediates. */
+ if (mo->pinfo2 & INSN2_ALLEGREX_PREFIX)
+ {
+ s = mips_vfpu_parse_prefix(s, &token.u.pfx_vfpu.prefixes[0],
+ &token.u.pfx_vfpu.num_chs);
+ if (s)
+ mips_add_token (&token, OT_PFX_VFPU);
+
+ return s;
+ }
+
/* Handle other characters that end up as OT_CHARs. */
if (*s == ')' || *s == ',')
{
@@ -3621,6 +3743,19 @@ mips_parse_argument_token (char *s, char float_format,
{
token.u.reg_vfpu.regno = regno1;
mips_add_token (&token, OT_REG_VFPU);
+
+ SKIP_SPACE_TABS (s);
+ if (*s == '[')
+ {
+ /* The register can feature a prefix, expressed as a suffix. */
+ s = mips_vfpu_parse_prefix (&s[1], &token.u.pfx_vfpu.prefixes[0],
+ &token.u.pfx_vfpu.num_chs);
+ if (s && *s == ']')
+ {
+ mips_add_token (&token, OT_PFX_VFPU);
+ return ++s;
+ }
+ }
return s;
}
@@ -5099,6 +5234,7 @@ operand_reg_mask (const struct mips_cl_insn *insn,
case OP_VU0_SUFFIX:
case OP_VU0_MATCH_SUFFIX:
case OP_VFPU_REG:
+ case OP_VFPU_PFX:
case OP_IMM_INDEX:
abort ();
@@ -6683,6 +6819,83 @@ match_vu0_suffix_operand (struct mips_arg_info *arg,
return true;
}
+/* Generates a VFPU prefix immediate for a given reg type and size. */
+static bfd_boolean
+calculate_vfpu_prefix_imm (struct mips_arg_info *arg,
+ enum mips_vfpu_reg_type regtype, unsigned int rsize,
+ unsigned int *immediate)
+{
+ unsigned int i;
+ unsigned imm24 = 0;
+ unsigned char *pfxchan = &arg->token->u.pfx_vfpu.prefixes[0];
+
+ for (i = 0; i <= rsize; i++)
+ {
+ if (pfxchan[i] == 0xff)
+ {
+ if (regtype == OP_VFPU_REG_D)
+ pfxchan[i] = PFX_DST;
+ else
+ pfxchan[i] = i; /* Natural swizzle when not specified. */
+ }
+
+ if (regtype == OP_VFPU_REG_D)
+ {
+ if (!(pfxchan[i] & PFX_DST))
+ {
+ set_insn_error (arg->argnum,
+ _("invalid destination prefix (can only contain masking/saturation)"));
+ return false;
+ }
+ }
+ else
+ {
+ if (pfxchan[i] & PFX_DST)
+ {
+ set_insn_error (arg->argnum,
+ _("invalid source prefix (can only contain constant/swizzle/neg/abs)"));
+ return false;
+ }
+ if (!(pfxchan[i] & 0x8) && (pfxchan[i] & 3) > rsize)
+ {
+ set_insn_error (arg->argnum,
+ _("invalid swizzle operation (out of range)"));
+ return false;
+ }
+ }
+
+ imm24 |= ((pfxchan[i] & 3) << (i * 2))
+ | ((pfxchan[i] & 0x4) << (6 + i))
+ | ((pfxchan[i] & 0x8) << (9 + i))
+ | ((pfxchan[i] & 0x10) << (12 + i));
+ }
+
+ *immediate = imm24;
+ return true;
+}
+
+/* Matches VFPU prefix operands. */
+static bfd_boolean
+match_vfpu_prefix_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned imm24;
+ const struct mips_vfpu_reg_operand *regvfpuop;
+ if (arg->token->type != OT_PFX_VFPU)
+ return false;
+
+ regvfpuop = (struct mips_vfpu_reg_operand*)operand;
+
+ if (!calculate_vfpu_prefix_imm (arg, regvfpuop->regtype, regvfpuop->rsize,
+ &imm24))
+ return false;
+
+ insn_insert_operand (arg->insn, operand, imm24);
+
+ ++arg->token;
+ return true;
+}
+
/* Matches VFPU register operands. */
static bfd_boolean
match_vfpu_reg_operand (struct mips_arg_info *arg,
@@ -6719,8 +6932,39 @@ match_vfpu_reg_operand (struct mips_arg_info *arg,
}
insn_insert_operand (arg->insn, operand, uval);
-
++arg->token;
+
+ /* Check if this register has an associated prefix. */
+ if (arg->token->type == OT_PFX_VFPU)
+ {
+ unsigned int imm24;
+ struct mips_cl_insn pfxinstr;
+ struct mips_opcode *pop;
+ bfd_reloc_code_real_type unused_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ const char *pfxmemo = regvfpuop->regtype == OP_VFPU_REG_S ? "vpfxs" :
+ regvfpuop->regtype == OP_VFPU_REG_T ? "vpfxt" : "vpfxd";
+
+ if (regvfpuop->rsize + 1 != arg->token->u.pfx_vfpu.num_chs)
+ {
+ set_insn_error (arg->argnum, _("invalid number of prefix elements"));
+ return false;
+ }
+
+ /* Create a prefix instruction that precedes this one. */
+ pop = (struct mips_opcode *) str_hash_find (op_hash, pfxmemo);
+ create_insn (&pfxinstr, pop);
+
+ if (!calculate_vfpu_prefix_imm (arg, regvfpuop->regtype, regvfpuop->rsize,
+ &imm24))
+ return false;
+
+ insn_insert_operand (&pfxinstr, decode_mips_operand (pop->args), imm24);
+ append_insn (&pfxinstr, NULL, unused_reloc, false);
+ ++arg->token;
+ }
+
return true;
}
@@ -6794,6 +7038,9 @@ match_operand (struct mips_arg_info *arg,
case OP_VFPU_REG:
return match_vfpu_reg_operand (arg, operand);
+ case OP_VFPU_PFX:
+ return match_vfpu_prefix_operand (arg, operand);
+
case OP_IMM_INDEX:
return match_imm_index_operand (arg, operand);
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.d b/gas/testsuite/gas/mips/allegrex-vfpu-errors.d
new file mode 100644
index 00000000000..de8edaafdf7
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.d
@@ -0,0 +1,3 @@
+#name: Allegrex VFPU assembly errors
+#as: -march=allegrex -mabi=32
+#error_output: allegrex-vfpu-errors.l
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.l b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
new file mode 100644
index 00000000000..aa10cf49e75
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
@@ -0,0 +1,10 @@
+.*: Assembler messages:
+.*:4: Error: invalid number of prefix elements `vadd.q R000\[,m,\],C000,R002'
+.*:5: Error: invalid swizzle operation \(out of range\) `vadd.t R000,C000,R002\[w,x,y\]'
+.*:6: Error: invalid number of prefix elements `vmul.q R000,C000,R002\[,y,z\]'
+.*:7: Error: invalid VFPU prefix `vsub.s S000,S000,S000\[p\]'
+.*:8: Error: invalid VFPU prefix, expecting ',' or '\]' `vsub.s S000,S000,S000\[1/7\]'
+.*:9: Error: unmatched '|' in VFPU prefix `vsub.s S000,S000,S000\[|x\]'
+.*:10: Error: invalid VFPU prefix, expecting ',' or '\]' `vsub.s S000,S000,S000\[xx\]'
+.*:11: Error: invalid source prefix \(can only contain constant/swizzle/neg/abs\) `vsub.t R000,R000,R000\[m,m,\]'
+.*:12: Error: invalid destination prefix \(can only contain masking/saturation\) `vsub.t R000\[x,y,z\],R000,R000'
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.s b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
new file mode 100644
index 00000000000..c2fa6dda856
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
@@ -0,0 +1,16 @@
+ .text
+ .set noreorder
+
+ vadd.q R000[,m,], C000, R002
+ vadd.t R000, C000, R002[w,x,y]
+ vmul.q R000, C000, R002[,y,z]
+ vsub.s S000, S000, S000[p]
+ vsub.s S000, S000, S000[1/7]
+ vsub.s S000, S000, S000[|x]
+ vsub.s S000, S000, S000[xx]
+ vsub.t R000, R000, R000[m,m,]
+ vsub.t R000[x,y,z], R000, R000
+
+# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
+ .align 4, 0
+ .space 16
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.d b/gas/testsuite/gas/mips/allegrex-vfpu.d
index e5fb8e13595..c39541364d5 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.d
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.d
@@ -63,4 +63,20 @@ Disassembly of section .text:
0x000000d8 d00600e4 vzero.p R120.p
0x000000dc d0068028 vzero.t R200.t
0x000000e0 d00680a8 vzero.q R200.q
+0x000000e4 dc0000e4 vpfxs x,y,z,w
+0x000000e8 dd0000e4 vpfxt x,y,z,w
+0x000000ec dc000000 vpfxs x,x,x,x
+0x000000f0 dc08dc30 vpfxs 0,x,1/6,-3
+0x000000f4 dc0305b3 vpfxs -|w|,-x,|w|,z
+0x000000f8 de000000 vpfxd ,,,
+0x000000fc de000174 vpfxd m,\[0:1\],\[-1:1\],\[0:1\]
+0x00000100 de000a00 vpfxd ,m,,m
+0x00000104 602280a0 vadd.q R000.q,C000.q,R002.q
+0x00000108 dc000050 vpfxs x,x,y,y
+0x0000010c dd0000fa vpfxt z,z,w,w
+0x00000110 602280a0 vadd.q R000.q,C000.q,R002.q
+0x00000114 de000200 vpfxd ,m,,
+0x00000118 dc00000a vpfxs z,z,x,x
+0x0000011c dd000018 vpfxt x,z,y,x
+0x00000120 64228020 vmul.t R000.t,C000.t,R002.t
\.\.\.
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.s b/gas/testsuite/gas/mips/allegrex-vfpu.s
index 7bbe5db61b9..228c17229f8 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.s
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.s
@@ -60,6 +60,17 @@
vzero.t R200
vzero.q R200
+ vpfxs ,,,
+ vpfxt ,,,
+ vpfxs x,x,x,x
+ vpfxs 0,x,1/6,-3
+ vpfxs -|w|, -x,| w |, z
+ vpfxd ,,,
+ vpfxd m,0:1,[-1:1],[0:1]
+ vadd.q R000[,m,,m], C000, R002
+ vadd.q R000, C000[x,x,y,y], R002[z,z,w,w]
+ vmul.t R000[,m,], C000[z,z,x], R002[x,z,y]
+
# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
.align 4, 0
.space 16
diff --git a/gas/testsuite/gas/mips/allegrex@isa-override-1.d b/gas/testsuite/gas/mips/allegrex@isa-override-1.d
index 99c9c129159..57e0123ee05 100644
--- a/gas/testsuite/gas/mips/allegrex@isa-override-1.d
+++ b/gas/testsuite/gas/mips/allegrex@isa-override-1.d
@@ -10,7 +10,7 @@ Disassembly of section \.text:
[0-9a-f]+ <[^>]*> 8c830004 lw v1,4\(a0\)
[0-9a-f]+ <[^>]*> 3c0189ab lui at,0x89ab
[0-9a-f]+ <[^>]*> 00411025 or v0,v0,at
-[0-9a-f]+ <[^>]*> dc820000 .word 0xdc820000
+[0-9a-f]+ <[^>]*> dc820000 vpfxs x,-x,x,x
[0-9a-f]+ <[^>]*> 340189ab li at,0x89ab
[0-9a-f]+ <[^>]*> 00010c38 .word 0x10c38
[0-9a-f]+ <[^>]*> 00411025 or v0,v0,at
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index 15aabc44e2a..f6f3a8cf0a1 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -1714,6 +1714,7 @@ if { [istarget mips*-*-vxworks*] } {
run_dump_test "allegrex"
run_dump_test "allegrex-removed"
run_dump_test "allegrex-vfpu"
+ run_dump_test "allegrex-vfpu-errors"
run_list_test_arches "ext-ill" [mips_arch_list_matching mips64r2]
diff --git a/include/opcode/mips.h b/include/opcode/mips.h
index 7c177f6a283..f535591e54d 100644
--- a/include/opcode/mips.h
+++ b/include/opcode/mips.h
@@ -171,6 +171,9 @@ enum mips_operand_type {
/* A VFPU register. */
OP_VFPU_REG,
+ /* A VFPU register prefix. */
+ OP_VFPU_PFX,
+
/* An index selected by an integer, e.g. [1]. */
OP_IMM_INDEX,
@@ -915,6 +918,8 @@ mips_opcode_32bit_p (const struct mips_opcode *mo)
encoding is needed or otherwise the final EXTEND entry will apply,
for the disassembly of the prefix only. */
#define INSN2_SHORT_ONLY 0x00010000
+/* Has special encoded prefix expressions that require parsing. */
+#define INSN2_ALLEGREX_PREFIX 0x00020000
/* Masks used to mark instructions to indicate which MIPS ISA level
they were introduced in. INSN_ISA_MASK masks an enumeration that
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c
index 7ed84a952a4..ed421e2aa71 100644
--- a/opcodes/mips-dis.c
+++ b/opcodes/mips-dis.c
@@ -1381,6 +1381,56 @@ print_vfpu_reg (struct disassemble_info *info,
}
}
+/* Print OP_VFPU_PFX operand OPERAND, whose value is given by UVAL. */
+static void
+print_vfpu_pfx (struct disassemble_info *info,
+ const struct mips_operand *operand, unsigned int uval)
+{
+ unsigned int i;
+ const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+ void *is = info->stream;
+ const struct mips_vfpu_reg_operand *vfpureg_op;
+
+ vfpureg_op = (const struct mips_vfpu_reg_operand *) operand;
+
+ for (i = 0; i <= vfpureg_op->rsize; i++)
+ {
+ if (i)
+ infprintf (is, dis_style_mnemonic, ",");
+
+ if (vfpureg_op->regtype == OP_VFPU_REG_D)
+ {
+ if (uval & (1 << (8 + i)))
+ infprintf (is, dis_style_mnemonic, "m");
+ else
+ {
+ static const char * const satimms[] = { "", "[0:1]", "", "[-1:1]" };
+ unsigned int sat = (uval >> (i * 2)) & 3;
+ infprintf (is, dis_style_mnemonic, "%s", satimms[sat]);
+ }
+ }
+ else
+ {
+ static const char * const pfxswz_str = "xyzw";
+ static const char * const pfxcst_str[2][4] = {
+ { "0", "1", "2", "1/2" }, { "3", "1/3", "1/4", "1/6" }
+ };
+ unsigned int hib = (uval >> (8 + i)) & 1;
+ unsigned int lob = (uval >> (i * 2)) & 3;
+
+ if (uval & (1 << (16 + i)))
+ infprintf (is, dis_style_mnemonic, "-");
+
+ if (uval & (1 << (12 + i)))
+ infprintf (is, dis_style_mnemonic, "%s", pfxcst_str[hib][lob]);
+ else if (hib)
+ infprintf (is, dis_style_mnemonic, "|%c|", pfxswz_str[lob]);
+ else
+ infprintf (is, dis_style_mnemonic, "%c", pfxswz_str[lob]);
+ }
+ }
+}
+
/* Record information about a register operand. */
@@ -1595,6 +1645,10 @@ print_insn_arg (struct disassemble_info *info,
print_vfpu_reg (info, operand, uval);
break;
+ case OP_VFPU_PFX:
+ print_vfpu_pfx (info, operand, uval);
+ break;
+
case OP_PERF_REG:
infprintf (is, dis_style_register, "%d", uval);
break;
@@ -1935,6 +1989,7 @@ validate_insn_args (const struct mips_opcode *opcode,
case OP_REG_INDEX:
case OP_SAVE_RESTORE_LIST:
case OP_VFPU_REG:
+ case OP_VFPU_PFX:
break;
}
}
diff --git a/opcodes/mips-formats.h b/opcodes/mips-formats.h
index 87d85e06dae..d31a514d51e 100644
--- a/opcodes/mips-formats.h
+++ b/opcodes/mips-formats.h
@@ -112,17 +112,23 @@
return &op.root; \
}
-#define VFPU_REG(SIZE, LSB, REGTYPE, REGSIZE) \
+#define VFPU_REGEXP(OP_TYPE, SIZE, LSB, REGTYPE, REGSIZE) \
{ \
static const struct mips_vfpu_reg_operand op[] = { \
- { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 0 }, \
- { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 1 }, \
- { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 2 }, \
- { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 3 }, \
+ { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 0 }, \
+ { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 1 }, \
+ { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 2 }, \
+ { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 3 }, \
}; \
return &op[REGSIZE].root; \
}
+#define VFPU_REG(SIZE, LSB, REGTYPE, REGSIZE) \
+ VFPU_REGEXP(OP_VFPU_REG, SIZE, LSB, REGTYPE, REGSIZE)
+
+#define VFPU_PFX(SIZE, LSB, REGTYPE, REGSIZE) \
+ VFPU_REGEXP(OP_VFPU_PFX, SIZE, LSB, REGTYPE, REGSIZE)
+
#define PCREL(SIZE, LSB, IS_SIGNED, SHIFT, ALIGN_LOG2, INCLUDE_ISA_BIT, \
FLIP_ISA_BIT) \
{ \
diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c
index f9341c94af9..e8c0418f1ca 100644
--- a/opcodes/mips-opc.c
+++ b/opcodes/mips-opc.c
@@ -148,6 +148,13 @@ decode_mips_operand (const char *p)
case 'd': VFPU_REG(7, 0, D, p[2] - '0');
case 's': VFPU_REG(7, 8, S, p[2] - '0');
case 't': VFPU_REG(7, 16, T, p[2] - '0');
+ case 'p':
+ switch (p[2])
+ {
+ case 'd': VFPU_PFX(24, 0, D, 3);
+ case 's': VFPU_PFX(24, 0, S, 3);
+ case 't': VFPU_PFX(24, 0, T, 3);
+ }
}
break;
@@ -367,6 +374,9 @@ decode_mips_operand (const char *p)
#define VU0 EE
#define VU0CH INSN2_VU0_CHANNEL_SUFFIX
+/* Support for Allegrex VFPU Coprocessor instructions */
+#define ALXPFX INSN2_ALLEGREX_PREFIX
+
/* MIPS DSP ASE support.
NOTE:
1. MIPS DSP ASE includes 4 accumulators ($ac0 - $ac3). $ac0 is the pair
@@ -709,6 +719,9 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"vmul.q", "?d3,?s3,?t3", 0x64008080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmul.s", "?d0,?s0,?t0", 0x64000000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmul.t", "?d2,?s2,?t2", 0x64008000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
+{"vpfxd", "?pd", 0xde000000, 0xff000000, CP, ALXPFX, ALX, 0, 0 },
+{"vpfxs", "?ps", 0xdc000000, 0xff000000, CP, ALXPFX, ALX, 0, 0 },
+{"vpfxt", "?pt", 0xdd000000, 0xff000000, CP, ALXPFX, ALX, 0, 0 },
{"vsge.p", "?d1,?s1,?t1", 0x6f000080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vsge.q", "?d3,?s3,?t3", 0x6f008080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vsge.s", "?d0,?s0,?t0", 0x6f000000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
--
2.51.1
More information about the Binutils
mailing list