[PATCH 1/2] target/sh4: add tests for sh4 fipr and ftrv instructions

Randy Schifflin <[email protected]> Mon, 29 Jun 2026 14:49:12 -0700
Newsgroups org.nongnu.qemu-trivial,org.nongnu.qemu-devel
Message-ID <20260629-fixup-sh4-tcg-fpu-instructions-b4-v1-1-4356b305f971@gmail.com>
Add tests for the TCG generation of the sh4-specific `fipr`
and `ftrv` instructions.

Signed-off-by: Randy Schifflin <[email protected]>
---
 tests/tcg/sh4/Makefile.target |  7 +++
 tests/tcg/sh4/test-fpu-fipr.c | 64 ++++++++++++++++++++++++++++
 tests/tcg/sh4/test-fpu-ftrv.c | 99 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+)

diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target
index b7a8737be0..0975fac72d 100644
--- a/tests/tcg/sh4/Makefile.target
+++ b/tests/tcg/sh4/Makefile.target
@@ -16,3 +16,10 @@ TESTS += test-addv
 
 test-subv: CFLAGS += -O -g
 TESTS += test-subv
+
+test-fpu-fipr: CFLAGS += -O -g
+TESTS += test-fpu-fipr
+
+test-fpu-ftrv: CFLAGS += -O -g
+TESTS += test-fpu-ftrv
+
diff --git a/tests/tcg/sh4/test-fpu-fipr.c b/tests/tcg/sh4/test-fpu-fipr.c
new file mode 100644
index 0000000000..bf8258fc36
--- /dev/null
+++ b/tests/tcg/sh4/test-fpu-fipr.c
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits.h>
+#include <stdlib.h>
+static const unsigned long FPSCR_PR_BIT = 1UL << 19;
+
+static void single_precision_mode(void) {
+    unsigned long fpscr;
+    // Read FPSCR register
+    asm volatile("sts fpscr, %0" : "=r" (fpscr));
+    // Set precision mode to single-precision
+    fpscr &= ~FPSCR_PR_BIT;
+    // Write FPSCR register
+    asm volatile("lds %0, fpscr" : : "r" (fpscr)); 
+}
+
+static float fipr(const float v1[4], const float v2[4]) {
+    float fout = 0.0;
+    // Perform inner product of fv4 and fv8
+    // Result is stored in the last float register of the last vector argument
+    asm volatile(
+        "fmov %1,fr4\n"
+        "fmov %2,fr5\n"
+        "fmov %3,fr6\n"
+        "fmov %4,fr7\n"
+        "fmov %5,fr8\n"
+        "fmov %6,fr9\n"
+        "fmov %7,fr10\n"
+        "fmov %8,fr11\n"
+        "fipr	fv4,fv8\n" // (fr4,fr5,fr6,fr7) dot (fr8,fr9,fr10,fr11)
+        "fmov fr11,%0\n"   // Result in fr11
+        : "=f" (fout) 
+        : "f" (v1[0]), "f" (v1[1]), "f" (v1[2]), "f" (v1[3]), "f" (v2[0]), "f" (v2[1]), "f" (v2[2]), "f" (v2[3]));
+    return fout;
+}
+
+int main(void)
+{
+    // The SH4 manual specifies `fipr` is only available when FPSCR register precision-mode (PR) bit is 0
+    single_precision_mode();
+
+    float v1[4] = {1.0, 0.0, 0.0, 0.0};
+    float mag_sq = 0.0;
+    float dot = 0.0;
+
+    mag_sq = fipr(v1, v1);
+    if (mag_sq != 1.0) {
+        abort();
+    }
+
+    v1[0] = 2.0;
+    mag_sq = fipr(v1, v1);
+    if (mag_sq != 4.0) {
+        abort();
+    }
+
+    float v2[4] = {1.0, 0.0, 0.0, 0.0};
+    dot = fipr(v1, v2);
+    if (dot != 2.0) {
+        abort();
+    }
+
+    return 0;
+}
diff --git a/tests/tcg/sh4/test-fpu-ftrv.c b/tests/tcg/sh4/test-fpu-ftrv.c
new file mode 100644
index 0000000000..1018da1b7c
--- /dev/null
+++ b/tests/tcg/sh4/test-fpu-ftrv.c
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include <limits.h>
+#include <stdlib.h>
+static const unsigned long FPSCR_PR_BIT = 1UL << 19;
+
+static void single_precision_mode(void) {
+    unsigned long fpscr;
+    // Read FPSCR register
+    asm volatile("sts fpscr, %0" : "=r" (fpscr));
+    // Set precision mode to single-precision
+    fpscr &= ~FPSCR_PR_BIT;
+    // Write FPSCR register
+    asm volatile("lds %0, fpscr" : : "r" (fpscr)); 
+}
+
+// Set XMTRX registers to a doubling scale matrix
+static void xmtrx_scale_2(void) {
+    asm volatile(
+        "fldi0 fr0\n" // Pair dr0 (fr0, fr1) = zero,zero
+        "fldi0 fr1\n"
+        "fldi1 fr2\n"
+        "fadd fr2,fr2\n" // Pair dr2 (fr2,fr3) = two,zero
+        "fldi0 fr3\n"
+        "fldi0 fr4\n"
+        "fmov fr2,fr5\n" // Pair dr4 (fr4, fr5) = zero,two
+
+        // Enable pair-write
+        "fschg\n" 
+        // matrix[0][0] = 2.0
+        // matrix[0][1] = 0.0
+        "fmov dr2,xd0\n" 
+        // matrix[0][2] = 0.0
+        // matrix[0][3] = 0.0
+        "fmov dr0,xd2\n"
+        // matrix[1][0] = 0.0
+        // matrix[1][1] = 2.0
+        "fmov dr4,xd4\n"
+        // matrix[1][2] = 0.0
+        // matrix[1][3] = 0.0
+        "fmov dr0,xd6\n"
+        // matrix[2][0] = 0.0
+        // matrix[2][1] = 0.0
+        "fmov dr0,xd8\n"
+        // matrix[2][2] = 2.0
+        // matrix[2][3] = 0.0
+        "fmov dr2,xd10\n"
+        // matrix[3][0] = 0.0
+        // matrix[3][1] = 0.0
+        "fmov dr0,xd12\n"
+        // matrix[3][2] = 0.0
+        // matrix[3][3] = 2.0
+        "fmov dr4,xd14\n"
+        // Disable pair-write
+        "fschg"
+        :
+        :
+        : "fr0", "fr1", "fr2", "fr3", "fr4", "fr5");
+}
+
+// The SH4 manual specifies `ftrv` is only available when FPSCR register precision-mode (PR) bit is 0, aka single-precision
+// Transform vector vin by xmtrx; store result in vector vout
+static void ftrv(const float vin[4], float vout[4]) {
+    asm volatile("fmov %4,fr0\n" 
+                 "fmov %5,fr1\n" 
+                 "fmov %6,fr2\n" 
+                 "fmov %7,fr3\n" 
+                 "ftrv xmtrx,fv0\n"
+                 "fmov fr0,%0\n" 
+                 "fmov fr1,%1\n" 
+                 "fmov fr2,%2\n" 
+                 "fmov fr3,%3" 
+                 : "=f" (vout[0]), "=f" (vout[1]), "=f" (vout[2]), "=f" (vout[3])
+                 : "f" (vin[0]), "f" (vin[1]), "f" (vin[2]), "f" (vin[3])
+                 : "fr0", "fr1", "fr2", "fr3");
+}
+
+int main(void)
+{
+    single_precision_mode();
+    xmtrx_scale_2();
+    float vin[4] = {1.0, 2.0, 3.0, 4.0};
+    float vout[4] = { 0.0, 0.0, 0.0, 0.0};
+
+    ftrv(vin, vout);
+    if (vout[0] != 2.0) {
+        abort();
+    }
+    if (vout[1] != 4.0) {
+        abort();
+    }
+    if (vout[2] != 6.0) {
+        abort();
+    }
+    if (vout[3] != 8.0) {
+        abort();
+    }
+
+    return 0;
+}

-- 
2.43.0