[PATCH v2 8/9] Add MIPS Allegrex VFPU coprocessor registers and instructions
David Guillen Fandos
david@davidgf.net
Tue Jan 20 23:37:24 GMT 2026
Adds move instructions from/to coprocessor registers as well as compare
instructions (that also update certain coprocessor registers). Add a
special move-constant instruction that loads well-known values.
These instructions require parsing some built-in string constants, such
as the comparison condition codes or the constant value names.
Signed-off-by: David Guillen Fandos <david@davidgf.net>
---
gas/config/tc-mips.c | 93 ++++++++++++++++++-
gas/testsuite/gas/mips/allegrex-vfpu-errors.l | 6 ++
gas/testsuite/gas/mips/allegrex-vfpu-errors.s | 6 ++
gas/testsuite/gas/mips/allegrex-vfpu.d | 14 +++
gas/testsuite/gas/mips/allegrex-vfpu.s | 15 +++
include/opcode/mips.h | 13 ++-
opcodes/mips-dis.c | 30 ++++++
opcodes/mips-opc.c | 23 +++++
8 files changed, 197 insertions(+), 3 deletions(-)
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 3541179caf8..64e6ed78bee 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -2709,6 +2709,7 @@ struct regname {
#define RTYPE_R5900_R 0x0200000
#define RTYPE_R5900_ACC 0x0400000
#define RTYPE_MSA 0x0800000
+#define RTYPE_VFPUCC 0x1000000
#define RWARN 0x8000000
#define GENERIC_REGISTER_NUMBERS \
@@ -3118,6 +3119,24 @@ struct regname {
{"$ac2", RTYPE_ACC | 2}, \
{"$ac3", RTYPE_ACC | 3}
+#define VFPU_COPROCESSOR_NUMBERS \
+ {"$128", RTYPE_VFPUCC | 128}, \
+ {"$129", RTYPE_VFPUCC | 129}, \
+ {"$130", RTYPE_VFPUCC | 130}, \
+ {"$131", RTYPE_VFPUCC | 131}, \
+ {"$132", RTYPE_VFPUCC | 132}, \
+ {"$133", RTYPE_VFPUCC | 133}, \
+ {"$134", RTYPE_VFPUCC | 134}, \
+ {"$135", RTYPE_VFPUCC | 135}, \
+ {"$136", RTYPE_VFPUCC | 136}, \
+ {"$137", RTYPE_VFPUCC | 137}, \
+ {"$138", RTYPE_VFPUCC | 138}, \
+ {"$139", RTYPE_VFPUCC | 139}, \
+ {"$140", RTYPE_VFPUCC | 140}, \
+ {"$141", RTYPE_VFPUCC | 141}, \
+ {"$142", RTYPE_VFPUCC | 142}, \
+ {"$143", RTYPE_VFPUCC | 143}
+
/* Register shapes for VFPU registers. */
enum mips_vfpu_reg_shape {
/* A vector register, number of elements from 1 to 4. */
@@ -3152,6 +3171,7 @@ static const struct regname reg_names[] = {
R5900_R_NAMES,
R5900_ACC_NAMES,
MIPS_DSP_ACCUMULATOR_NAMES,
+ VFPU_COPROCESSOR_NUMBERS,
{0, 0}
};
@@ -5761,6 +5781,9 @@ convert_reg_type (const struct mips_opcode *opcode,
case OP_REG_MSA_CTRL:
return RTYPE_NUM;
+
+ case OP_REG_VFPU_CC:
+ return RTYPE_VFPUCC;
}
abort ();
}
@@ -7101,7 +7124,8 @@ match_vfpu_reg_operand (struct mips_arg_info *arg,
}
if ((rtype == OP_VFPU_REG_S || rtype == OP_VFPU_REG_T)
- && (arg->dest_regno != ILLEGAL_REG))
+ && (arg->dest_regno != ILLEGAL_REG)
+ && !(arg->insn->insn_mo->pinfo & INSN_COPROC_MOVE))
{
/* The destination register could be incompatible with source regs. */
unsigned int dreg = arg->dest_regno & 0x7f;
@@ -7166,6 +7190,32 @@ match_vfpu_reg_operand (struct mips_arg_info *arg,
return true;
}
+/* Matches named constants or constant value. */
+static bfd_boolean
+match_vfpu_named_constant (const expressionS *exp, unsigned int *val,
+ const char * const *strlist, unsigned int listcnt)
+{
+ if (exp->X_op == O_symbol)
+ {
+ unsigned int i;
+ for (i = 0; i < listcnt; i++)
+ if (strlist[i] && !strcmp(S_GET_NAME(exp->X_add_symbol), strlist[i]))
+ {
+ *val = i;
+ return true;
+ }
+ }
+ else if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < listcnt)
+ {
+ *val = exp->X_add_number;
+ return true;
+ }
+ }
+ return false;
+}
+
/* Matches VFPU immediate operands. */
static bfd_boolean
match_vfpu_imm_operand (struct mips_arg_info *arg,
@@ -7212,6 +7262,47 @@ match_vfpu_imm_operand (struct mips_arg_info *arg,
++arg->token;
return true;
}
+ return false;
+
+ case OP_VFPU_COMP:
+ {
+ static const char * const cnds[16] = {
+ "FL", "EQ", "LT", "LE", "TR", "NE", "GE", "GT",
+ "EZ", "EN", "EI", "ES", "NZ", "NN", "NI", "NS"
+ };
+ if (arg->token->type == OT_INTEGER
+ && match_vfpu_named_constant (&arg->token->u.integer.value, &uval,
+ cnds, ARRAY_SIZE(cnds)))
+ {
+ insn_insert_operand (arg->insn, operand, uval);
+ ++arg->token;
+ return true;
+ }
+ }
+ break;
+
+ case OP_VFPU_CONST:
+ {
+ static const char * const knsts[] = {
+ NULL, "VFPU_HUGE", "VFPU_SQRT2", "VFPU_SQRT1_2",
+ "VFPU_2_SQRTPI", "VFPU_2_PI", "VFPU_1_PI", "VFPU_PI_4",
+ "VFPU_PI_2", "VFPU_PI", "VFPU_E", "VFPU_LOG2E",
+ "VFPU_LOG10E", "VFPU_LN2", "VFPU_LN10", "VFPU_2PI",
+ "VFPU_PI_6", "VFPU_LOG10TWO", "VFPU_LOG2TEN", "VFPU_SQRT3_2",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ };
+ if (arg->token->type == OT_INTEGER
+ && match_vfpu_named_constant (&arg->token->u.integer.value, &uval,
+ knsts, ARRAY_SIZE(knsts)))
+ {
+ insn_insert_operand (arg->insn, operand, uval);
+ ++arg->token;
+ return true;
+ }
+ }
+ break;
}
return false;
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.l b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
index 79f3a199e55..a87298aa63d 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
@@ -81,3 +81,9 @@
.*:83: Error: no prefixes allowed for s-register `vbfy2.q R000,R000\[x,x,y,y\]'
.*:84: Error: invalid source prefix \(only swizzle allowed\) `vi2c.q R000,R000\[-x,-y,-z,-w\]'
.*:85: Error: invalid source prefix \(only swizzle allowed\) `vi2uc.q R000,R000\[-x,-y,-z,-w\]'
+.*:86: Error: invalid operands `mfvc \$4,\$200'
+.*:87: Error: invalid operands `mtvc \$4,\$200'
+.*:88: Error: invalid operands `vmtvc \$200,S733'
+.*:89: Error: invalid operands `vcmp.q 123,C000,R000'
+.*:90: Error: invalid operands `vcmp.q foo,C000,R000'
+.*:91: Error: invalid operands `mfvc \$4,\$4'
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.s b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
index ce8d53a2285..baa7040499b 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
@@ -83,6 +83,12 @@
vbfy2.q R000, R000[x,x,y,y]
vi2c.q R000, R000[-x,-y,-z,-w]
vi2uc.q R000, R000[-x,-y,-z,-w]
+ mfvc $4,$200
+ mtvc $4,$200
+ vmtvc $200, S733
+ vcmp.q 123, C000, R000
+ vcmp.q foo, C000, R000
+ mfvc $4,$4
# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
.align 4, 0
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.d b/gas/testsuite/gas/mips/allegrex-vfpu.d
index b0c6191e61c..789e5ae6d17 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.d
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.d
@@ -275,4 +275,18 @@ Disassembly of section .text:
0x00000428 d03a7cbc vus2i.p R700.q,R720.p
0x0000042c d03a1e3c vus2i.s R700.p,S720.s
0x00000430 d39b2e66 vwbn.s S123.s,S321.s,0x9b
+0x00000434 48640080 mfvc \$4,\$128
+0x00000438 4864008c mfvc \$4,\$140
+0x0000043c 48e40080 mtvc \$4,\$128
+0x00000440 48e4008c mtvc \$4,\$140
+0x00000444 d0517f80 vmtvc \$128,S733.s
+0x00000448 d050807f vmfvc S733.s,\$128
+0x0000044c d0600000 vcst.s S000.s,0
+0x00000450 d07f8000 vcst.t C000.t,31
+0x00000454 d06800a0 vcst.p R000.p,VFPU_PI_2
+0x00000458 d06580a0 vcst.q R000.q,VFPU_2_PI
+0x0000045c 6c000001 vcmp.s EQ,S000.s,S000.s
+0x00000460 6c202082 vcmp.p LT,R000.p,R000.p
+0x00000464 6c20a001 vcmp.t EQ,R000.t,R000.t
+0x00000468 6c208085 vcmp.q NE,C000.q,R000.q
\.\.\.
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.s b/gas/testsuite/gas/mips/allegrex-vfpu.s
index 4299a1b020a..79d62dd3c35 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.s
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.s
@@ -264,6 +264,21 @@
vus2i.s R700.p, S720.s
vwbn.s S123, S321, 155
+ mfvc $4,$128
+ mfvc $4,$140
+ mtvc $4,$128
+ mtvc $4,$140
+ vmtvc $128, S733
+ vmfvc S733, $128
+ vcst.s S000, 0
+ vcst.t C000, 31
+ vcst.p R000, VFPU_PI_2
+ vcst.q R000, VFPU_2_PI
+ vcmp.s EQ, S000, S000
+ vcmp.p 2, R000, R000
+ vcmp.t 1, R000, R000
+ vcmp.q NE, C000, R000
+
# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
.align 4, 0
.space 16
diff --git a/include/opcode/mips.h b/include/opcode/mips.h
index 0d1b4b9377e..11367e2eb88 100644
--- a/include/opcode/mips.h
+++ b/include/opcode/mips.h
@@ -242,7 +242,10 @@ enum mips_reg_operand_type {
OP_REG_MSA,
/* MSA control registers $0-$31. */
- OP_REG_MSA_CTRL
+ OP_REG_MSA_CTRL,
+
+ /* VFPU control registers $0-$255. */
+ OP_REG_VFPU_CC
};
/* Enumerates the types of VFPU register. */
@@ -258,7 +261,13 @@ enum mips_vfpu_reg_type {
/* Enumerates the types of VFPU immediates. */
enum mips_vfpu_imm_type {
/* Half-float immediate (16 bit half-precision float). */
- OP_VFPU_FP16
+ OP_VFPU_FP16,
+
+ /* Compare condition codes. */
+ OP_VFPU_COMP,
+
+ /* VFPU LUT constant values. */
+ OP_VFPU_CONST
};
/* Enumerates the types of VFPU register-prefix compatibility. */
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c
index 6e9c5d18e30..f847ed4e8f9 100644
--- a/opcodes/mips-dis.c
+++ b/opcodes/mips-dis.c
@@ -1287,6 +1287,10 @@ print_reg (struct disassemble_info *info, const struct mips_opcode *opcode,
msa_control_names[regno]);
break;
+ case OP_REG_VFPU_CC:
+ infprintf (info->stream, dis_style_register, "$%d", regno);
+ break;
+
}
}
@@ -1496,6 +1500,32 @@ print_vfpu_imm (struct disassemble_info *info,
}
}
break;
+
+ case OP_VFPU_COMP:
+ {
+ static const char * const cnames[16] = {
+ "FL", "EQ", "LT", "LE", "TR", "NE", "GE", "GT",
+ "EZ", "EN", "EI", "ES", "NZ", "NN", "NI", "NS"
+ };
+ infprintf (is, dis_style_immediate, "%s", cnames[uval]);
+ }
+ break;
+
+ case OP_VFPU_CONST:
+ {
+ static const char * const knames[] = {
+ "HUGE", "SQRT2", "SQRT1_2",
+ "2_SQRTPI", "2_PI", "1_PI", "PI_4",
+ "PI_2", "PI", "E", "LOG2E",
+ "LOG10E", "LN2", "LN10", "2PI",
+ "PI_6", "LOG10TWO", "LOG2TEN", "SQRT3_2"
+ };
+ if (uval >= 1 && uval <= 19)
+ infprintf (is, dis_style_immediate, "VFPU_%s", knames[uval - 1]);
+ else
+ infprintf (is, dis_style_immediate, "%d", uval);
+ }
+ break;
}
}
diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c
index ab45735e20c..dd480a2a508 100644
--- a/opcodes/mips-opc.c
+++ b/opcodes/mips-opc.c
@@ -164,11 +164,21 @@ decode_mips_operand (const char *p)
switch (p[2])
{
case 'f': VFPU_IMM(16, 0, FP16);
+ case 'c': VFPU_IMM(4, 0, COMP);
+ case 'k': VFPU_IMM(5, 16, CONST);
case 's': UINT(5, 16);
case 'e': HINT(8, 16);
default:
abort();
}
+ case 'c':
+ switch (p[2])
+ {
+ case 'l': REG (8, 0, VFPU_CC);
+ case 'h': REG (8, 8, VFPU_CC);
+ default:
+ abort();
+ }
default:
abort();
}
@@ -705,6 +715,9 @@ const struct mips_opcode mips_builtin_opcodes[] =
/* Allegrex VFPU coprocessor. Redefines coprocessor 2 and 3 (and other unused opcodes). */
{"mfv", "t,?d0n", 0x48600000, 0xffe0ff80, WR_1|RD_C2, 0, ALX, 0, 0 },
{"mtv", "t,?d0n", 0x48e00000, 0xffe0ff80, RD_1|WR_C2, 0, ALX, 0, 0 },
+{"mfvc", "t,?cl.", 0x48600000, 0xffe0ff00, WR_1|RD_C2|CM, 0, ALX, 0, 0 },
+{"mtvc", "t,?cl.", 0x48e00000, 0xffe0ff00, RD_1|WR_C2|CM, 0, ALX, 0, 0 },
+
{"vabs.p", "?d1a,?s1l", 0xd0010080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vabs.q", "?d3a,?s3l", 0xd0018080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vabs.s", "?d0a,?s0l", 0xd0010000, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
@@ -724,12 +737,20 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"vbfy1.q", "?d3a,?s3n", 0xd0428080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vbfy2.q", "?d3a,?s3n", 0xd0438080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vc2i.s", "?d3l,?s0n", 0xd0390000, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
+{"vcmp.p", "?jc.,?s1a,?t1a", 0x6c000080, 0xff8080f0, RD_C2, 0, ALX, 0, 0 },
+{"vcmp.q", "?jc.,?s3a,?t3a", 0x6c008080, 0xff8080f0, RD_C2, 0, ALX, 0, 0 },
+{"vcmp.s", "?jc.,?s0a,?t0a", 0x6c000000, 0xff8080f0, RD_C2, 0, ALX, 0, 0 },
+{"vcmp.t", "?jc.,?s2a,?t2a", 0x6c008000, 0xff8080f0, RD_C2, 0, ALX, 0, 0 },
{"vcos.p", "?D1n,?s1n", 0xd0130080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vcos.q", "?D3n,?s3n", 0xd0138080, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vcos.s", "?D0n,?s0n", 0xd0130000, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vcos.t", "?D2n,?s2n", 0xd0138000, 0xffff8080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vcrsp.t", "?i2n,?s2n,?t2n", 0xf2808000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vcrs.t", "?d2a,?s2n,?t2n", 0x66808000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
+{"vcst.p", "?d1a,?jk.", 0xd0600080, 0xffe0ff80, WR_C2, 0, ALX, 0, 0 },
+{"vcst.q", "?d3a,?jk.", 0xd0608080, 0xffe0ff80, WR_C2, 0, ALX, 0, 0 },
+{"vcst.s", "?d0a,?jk.", 0xd0600000, 0xffe0ff80, WR_C2, 0, ALX, 0, 0 },
+{"vcst.t", "?d2a,?jk.", 0xd0608000, 0xffe0ff80, WR_C2, 0, ALX, 0, 0 },
{"vdet.p", "?d0a,?s1a,?t1n", 0x67000080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vdiv.p", "?D1n,?s1n,?t1n", 0x63800080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vdiv.q", "?D3n,?s3n,?t3n", 0x63808080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
@@ -795,6 +816,7 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"vmax.q", "?d3a,?s3a,?t3a", 0x6d808080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmax.s", "?d0a,?s0a,?t0a", 0x6d800000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmax.t", "?d2a,?s2a,?t2a", 0x6d808000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
+{"vmfvc", "?d0n,?ch.", 0xd0500000, 0xffff0080, RD_C2|WR_C2|CM, 0, ALX, 0, 0 },
{"vmidt.p", "?d4n", 0xf3830080, 0xffffff80, WR_C2, 0, ALX, 0, 0 },
{"vmidt.q", "?d6n", 0xf3838080, 0xffffff80, WR_C2, 0, ALX, 0, 0 },
{"vmidt.t", "?d5n", 0xf3838000, 0xffffff80, WR_C2, 0, ALX, 0, 0 },
@@ -818,6 +840,7 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"vmscl.p", "?D4n,?s4n,?t0n", 0xf2000080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmscl.q", "?D6n,?s6n,?t0n", 0xf2008080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmscl.t", "?D5n,?s5n,?t0n", 0xf2008000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
+{"vmtvc", "?cl.,?s0n", 0xd0510000, 0xffff8000, RD_C2|WR_C2|CM, 0, ALX, 0, 0 },
{"vmul.p", "?d1a,?s1a,?t1a", 0x64000080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmul.q", "?d3a,?s3a,?t3a", 0x64008080, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
{"vmul.s", "?d0a,?s0a,?t0a", 0x64000000, 0xff808080, RD_C2|WR_C2, 0, ALX, 0, 0 },
--
2.51.1
More information about the Binutils
mailing list