@samitouri / QOSamiQemu / commits / 0083c9cf2d

target/mips: add Octeon VMULU instruction

VMULU multiplies the active Octeon multiplier state by rs, adds rt and queued partial products, returns the low result, and advances P[0]/P[1] with carry limbs. Expand the two-limb accumulator operation inline with TCG so the result and partial-product state stay visible to the optimizer. Add a mips64/mips64el linux-user TCG smoke test for representative Octeon multiplier instruction paths. Include hardware-backed regression coverage for MTP0 P1 zeroing. Signed-off-by: James Hilliard <james.hilliard1@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20260520172313.23777-21-philmd@linaro.org>

James Hilliard committed May 8, 2026 at 10:55 UTC 0083c9cf2d592a9f9b8e90153cba084e7f229502
3 files changed +86
target/mips/tcg/octeon.decode
+2
@@ -55,6 +55,8 @@ MTP0 011100 ..... ..... 00000 00000 001001 @r2
55 MTP1 011100 ..... ..... 00000 00000 001010 @r2
56 MTP2 011100 ..... ..... 00000 00000 001011 @r2
57
58 +VMULU 011100 ..... ..... ..... 00000 001111 @r3
59 +
60 &saa base rt
61 @saa ...... base:5 rt:5 ................ &saa
62 SAA 011100 ..... ..... 00000 00000 011000 @saa
target/mips/tcg/octeon_translate.c
+35
@@ -264,3 +264,38 @@ static bool trans_mtp(DisasContext *ctx, arg_r2 *a, unsigned int index)
264 TRANS(MTP0, trans_mtp, 0);
265 TRANS(MTP1, trans_mtp, 1);
266 TRANS(MTP2, trans_mtp, 2);
267 +
268 +static bool trans_VMULU(DisasContext *ctx, arg_VMULU *a)
269 +{
270 + TCGv_i64 x[3], y[3], z[3];
271 + TCGv_i64 tmp = tcg_temp_new_i64();
272 + TCGv_i64 zero = tcg_constant_i64(0);
273 +
274 + z[0] = y[0] = tcg_temp_new_i64();
275 + z[1] = y[1] = tcg_temp_new_i64();
276 + z[2] = y[2] = tcg_temp_new_i64();
277 + x[0] = tcg_temp_new_i64();
278 + x[1] = tcg_temp_new_i64();
279 + x[2] = zero;
280 +
281 + /* Z = rs * (mpl1 : mpl0) + rt */
282 + gen_load_gpr(tmp, a->rs);
283 + gen_load_gpr(y[0], a->rt);
284 + tcg_gen_mulu2_i64(x[0], x[1], tmp, oct_mpl[0]);
285 + tcg_gen_mulu2_i64(y[1], y[2], tmp, oct_mpl[1]);
286 + tcg_gen_addN_i64(3, z, y, x);
287 +
288 + /* X == (0 : p1 : p0) */
289 + x[0] = oct_p[0];
290 + x[1] = oct_p[1];
291 +
292 + /* Y == (p1 : p0 : tmp) */
293 + y[0] = tmp;
294 + y[1] = oct_p[0];
295 + y[2] = oct_p[1];
296 +
297 + /* (p1 : p0 : rd) = Z + (0 : p1 : p0) */
298 + tcg_gen_addN_i64(3, y, z, x);
299 + gen_store_gpr(tmp, a->rd);
300 + return true;
301 +}
tests/tcg/mips/user/isa/octeon/octeon-insns.c
+49
@@ -86,6 +86,53 @@ static uint64_t octeon_sne(uint64_t rs, uint64_t rt)
86 return rd;
87 }
88
89 +static uint64_t octeon_vmulu(uint64_t mpl0, uint64_t rs, uint64_t rt)
90 +{
91 + uint64_t rd;
92 +
93 + asm volatile(
94 + "move $8, %[mpl0]\n\t"
95 + "move $9, $0\n\t"
96 + ".word 0x71090008\n\t" /* mtm0 $8, $9 */
97 + "move $8, %[rs]\n\t"
98 + "move $9, %[rt]\n\t"
99 + ".word 0x7109500f\n\t" /* vmulu $10, $8, $9 */
100 + "move %[rd], $10\n\t"
101 + : [rd] "=r" (rd)
102 + : [mpl0] "r" (mpl0), [rs] "r" (rs), [rt] "r" (rt)
103 + : "$8", "$9", "$10");
104 +
105 + return rd;
106 +}
107 +
108 +static uint64_t octeon_mtp0_zeroes_p1(void)
109 +{
110 + uint64_t rd;
111 +
112 + asm volatile(
113 + "move $8, %[mpl0]\n\t"
114 + "move $9, $0\n\t"
115 + ".word 0x71090008\n\t" /* mtm0 $8, $9 */
116 + "move $8, %[p1]\n\t"
117 + "move $9, $0\n\t"
118 + ".word 0x7109000a\n\t" /* mtp1 $8, $9 */
119 + "move $8, $0\n\t"
120 + "move $9, $0\n\t"
121 + ".word 0x71090009\n\t" /* mtp0 $8, $9 */
122 + "move $8, $0\n\t"
123 + "move $9, $0\n\t"
124 + ".word 0x7109500f\n\t" /* vmulu $10, $8, $9 */
125 + "move $8, $0\n\t"
126 + "move $9, $0\n\t"
127 + ".word 0x7109500f\n\t" /* vmulu $10, $8, $9 */
128 + "move %[rd], $10\n\t"
129 + : [rd] "=r" (rd)
130 + : [mpl0] "r" (0ULL), [p1] "r" (1ULL)
131 + : "$8", "$9", "$10");
132 +
133 + return rd;
134 +}
135 +
136 int main(void)
137 {
138 assert(octeon_baddu(0x123, 0x0f0) == 0x13);
@@ -95,6 +142,8 @@ int main(void)
142 assert(octeon_seq(0xabc, 0xdef) == 0);
143 assert(octeon_sne(0xabc, 0xabc) == 0);
144 assert(octeon_sne(0xabc, 0xdef) == 1);
145 + assert(octeon_vmulu(5, 7, 11) == 46);
146 + assert(octeon_mtp0_zeroes_p1() == 0);
147
148 return 0;
149 }