target/mips: add Octeon QMAC instructions
QMAC.0x and QMACS.0x multiply the selected signed Q15 halfword lane from rs by rt<15:0> and accumulate the Q31 product into the Octeon HI/LO accumulator state. QMAC updates the full 64-bit HI/LO accumulator. QMACS saturates the 32-bit Q31 result in LO and keeps HI<0> as the sticky saturation flag. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: James Hilliard <james.hilliard1@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMD: Add min32/max32 in trans_QMACS()] Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20260520172313.23777-24-philmd@linaro.org>
James Hilliard committed
May 11, 2026 at 12:22 UTC
710be4a71224f8cdecdd0dd69110a8099bc8a3e6
2 files changed
+66
target/mips/tcg/octeon.decode
+4
@@ -28,6 +28,8 @@ BBIT 11 set:1 . 10 rs:5 ..... offset:s16 p=%bbit_p
28
# SEQI rt, rs, immediate
29
# SNE rd, rs, rt
30
# SNEI rt, rs, immediate
31
+# QMAC.0x rs, rt
32
+# QMACS.0x rs, rt
33
34
@r3 ...... rs:5 rt:5 rd:5 ..... ......
35
&cmpi rs rt imm
@@ -43,6 +45,8 @@ SEQ 011100 ..... ..... ..... 00000 101010 @r3
45
SNE 011100 ..... ..... ..... 00000 101011 @r3
46
SEQI 011100 rs:5 rt:5 imm:s10 101110 &cmpi
47
SNEI 011100 rs:5 rt:5 imm:s10 101111 &cmpi
48
+QMACS 011100 rs:5 rt:5 00000 000 lane:2 010010
49
+QMAC 011100 rs:5 rt:5 00000 100 lane:2 010010
50
51
&r2 rs rt
52
@r2 ...... rs:5 rt:5 ..... ..... ...... &r2
target/mips/tcg/octeon_translate.c
+62
@@ -355,3 +355,65 @@ static bool trans_V3MULU(DisasContext *ctx, arg_V3MULU *a)
355
gen_store_gpr(tmp, a->rd);
356
return true;
357
}
358
+
359
+static bool trans_QMAC(DisasContext *ctx, arg_QMAC *a)
360
+{
361
+ TCGv_i64 t0 = tcg_temp_new_i64();
362
+ TCGv_i64 t1 = tcg_temp_new_i64();
363
+
364
+ gen_load_gpr(t0, a->rt);
365
+ gen_load_gpr(t1, a->rs);
366
+
367
+ /* t0 = rt<0> * rs<lane> * 2 */
368
+ tcg_gen_ext16s_i64(t0, t0);
369
+ tcg_gen_sextract_i64(t1, t1, a->lane * 16, 16);
370
+ tcg_gen_mul_i64(t0, t0, t1);
371
+ tcg_gen_add_i64(t0, t0, t0);
372
+
373
+ /* Saturate -0x8000 * -0x8000 * 2 = 0x80000000 -> 0x7fffffff */
374
+ tcg_gen_smin_i64(t0, t0, tcg_constant_i64(INT32_MAX));
375
+
376
+ /* HI:LO += t0 */
377
+ tcg_gen_concat32_i64(t1, cpu_LO[0], cpu_HI[0]);
378
+ tcg_gen_add_i64(t0, t0, t1);
379
+ tcg_gen_sextract_i64(cpu_LO[0], t0, 0, 32);
380
+ tcg_gen_sextract_i64(cpu_HI[0], t0, 32, 32);
381
+ return true;
382
+}
383
+
384
+static bool trans_QMACS(DisasContext *ctx, arg_QMACS *a)
385
+{
386
+ TCGv_i64 min32 = tcg_constant_i64(INT32_MIN);
387
+ TCGv_i64 max32 = tcg_constant_i64(INT32_MAX);
388
+ TCGv_i64 t0 = tcg_temp_new_i64();
389
+ TCGv_i64 t1 = tcg_temp_new_i64();
390
+
391
+ gen_load_gpr(t0, a->rt);
392
+ gen_load_gpr(t1, a->rs);
393
+
394
+ /* t0 = rt<0> * rs<lane> * 2 */
395
+ tcg_gen_ext16s_i64(t0, t0);
396
+ tcg_gen_sextract_i64(t1, t1, a->lane * 16, 16);
397
+ tcg_gen_mul_i64(t0, t0, t1);
398
+ tcg_gen_add_i64(t0, t0, t0);
399
+
400
+ /*
401
+ * Saturate -0x8000 * -0x8000 * 2 = 0x80000000 -> 0x7fffffff.
402
+ * Accumulate overflow in HI[0].
403
+ */
404
+ tcg_gen_smin_i64(t1, t0, max32);
405
+ tcg_gen_setcond_i64(TCG_COND_NE, t0, t0, t1);
406
+ tcg_gen_or_i64(cpu_HI[0], cpu_HI[0], t0);
407
+
408
+ /*
409
+ * LO = sat32(LO + t0)
410
+ * Accumulate overflow in HI[0].
411
+ */
412
+ tcg_gen_ext32s_i64(t0, cpu_LO[0]);
413
+ tcg_gen_add_i64(t0, t0, t1);
414
+ tcg_gen_smin_i64(cpu_LO[0], t0, max32);
415
+ tcg_gen_smax_i64(cpu_LO[0], cpu_LO[0], min32);
416
+ tcg_gen_setcond_i64(TCG_COND_NE, t0, t0, cpu_LO[0]);
417
+ tcg_gen_or_i64(cpu_HI[0], cpu_HI[0], t0);
418
+ return true;
419
+}