Re: [PATCH 1/2] target/sh4: add tcg tests for fmov instructions
[email protected] Thu, 30 Jul 2026 21:24:48 +0900
Newsgroups
gmane.comp.emulators.qemu
Message-ID
<[email protected] >
On Thu, 30 Jul 2026 05:55:07 +0900,
Randy Schifflin wrote:
>
> Add tests for the TCG generation of the sh4-specific `fmov`
> instructions. Covers register/register and register/memory
> in each direction, for single and double precision fp registers,
> and with direct and indirect memory addressing.
>
> Signed-off-by: Randy Schifflin <[email protected] >
> ---
> tests/tcg/sh4/Makefile.target | 3 +
> tests/tcg/sh4/test-fpu-fmov.c | 518 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 521 insertions(+)
>
> diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target
> index b7a8737be0..82f4898f90 100644
> --- a/tests/tcg/sh4/Makefile.target
> +++ b/tests/tcg/sh4/Makefile.target
> @@ -16,3 +16,6 @@ TESTS += test-addv
>
> test-subv: CFLAGS += -O -g
> TESTS += test-subv
> +
> +test-fpu-fmov: CFLAGS += -O -g
> +TESTS += test-fpu-fmov
> diff --git a/tests/tcg/sh4/test-fpu-fmov.c b/tests/tcg/sh4/test-fpu-fmov.c
> new file mode 100644
> index 0000000000..90e51923ed
> --- /dev/null
> +++ b/tests/tcg/sh4/test-fpu-fmov.c
> @@ -0,0 +1,518 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +
> +#define ARR_SIZE 16
> +static const float in_arr[ARR_SIZE] = {
> + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
> + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0
> +};
> +
> +static float out_arr[ARR_SIZE] = {
> + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
> + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
> +};
> +
> +static const unsigned long FPSCR_PR_BIT = 1UL << 19;
> +static void single_precision_mode(void)
> +{
> + unsigned long fpscr;
> + asm volatile("sts fpscr, %0" : "=r" (fpscr));
> + fpscr &= ~FPSCR_PR_BIT;
> + asm volatile("lds %0, fpscr" : : "r" (fpscr));
> +}
> +
> +static void double_precision_mode(void)
> +{
> + unsigned long fpscr;
> + asm volatile("sts fpscr, %0" : "=r" (fpscr));
> + fpscr |= FPSCR_PR_BIT;
> + asm volatile("lds %0, fpscr" : : "r" (fpscr));
> +}
> +
> +static void fill_arr(float *arr, float n)
> +{
> + for (int i = 0; i < ARR_SIZE; i++) {
> + arr[i] = n;
> + }
> +}
> +
> +/* uses fmov frm,frn instruction */
> +static int test_fmov_freg_freg(void)
> +{
> + int pass = true;
> +
> + register float out asm ("fr0") = 0.0;
> + asm volatile(
> + "fldi1 fr1\n"
> + "fmov fr1,%0\n"
> + : "=f" (out)
> + );
> +
> + if (out != 1.0) {
> + fprintf(
> + stderr,
> + "fmov freg,freg failed. Expected %f, got %f\n", 1.0, out
> + );
> + pass = false;
> + }
> +
> + return pass;
> +}
> +
> +/* uses fmov frm,@rn instruction */
> +static int test_fmov_freg_addr(float *out)
> +{
> + int pass = true;
> + fill_arr(out, 0);
> +
> + register float *out_addr = out;
> + asm volatile(
> + "fldi1 fr0\n"
> + "fmov.s fr0,@%0\n"
> + : : "r" (out_addr) : "memory"
> + );
> +
> + if (out[0] != 1.0) {
> + fprintf(
> + stderr,
> + "fmov freg,addr failed. Expected %f, got %f\n", 1.0, out[0]
> + );
> + pass = false;
> + }
> +
> + fill_arr(out, 0);
> + return pass;
> +}
> +
> +/* uses fmov @rm,frn instruction */
> +static int test_fmov_addr_freg(const float *in)
> +{
> + int pass = true;
> + const register float *in_addr = in;
> + register float out = 0.0;
> + asm volatile(
> + "fmov.s @%1,%0\n"
> + : "=f" (out) : "r" (in_addr)
> + );
> +
> + if (out != 1.0) {
> + fprintf(
> + stderr,
> + "fmov addr,freg failed. Expected %f, got %f\n", 1.0, out
> + );
> + pass = false;
> + }
> +
> + return pass;
> +}
> +
> +/* uses fmov frm,@(r0,rn) instruction */
> +static int test_fmov_freg_indirect_addr(float *out)
> +{
> + int pass = true;
> + fill_arr(out, 0.0);
> +
> + register float *out_addr = out;
> + asm volatile(
> + "mov #4,r0\n"
> + "fldi1 fr0\n"
> + "fmov.s fr0, @(r0, %0)\n"
> + : : "r" (out_addr) : "memory"
> + );
> +
> + if (out[1] != 1.0) {
> + fprintf(
> + stderr,
> + "fmov freg,indirect_addr failed. Expected %f, got %f\n", 1.0, out[1]
> + );
> + pass = false;
> + }
> +
> + fill_arr(out, 0.0);
> + return pass;
> +}
> +
> +/* uses fmov @(r0,rm),frn instruction */
> +static int test_fmov_indirect_addr_freg(const float *in)
> +{
> + int pass = true;
> +
> + const register float *in_addr asm ("r1") = in;
> + register float out asm ("fr0") = 0.0;
> + asm volatile(
> + "mov #4,r0\n"
> + "fmov.s @(r0, %1), %0\n"
> + : "=f" (out) : "r" (in_addr) : "memory"
> + );
> +
> + if (out != in[1]) {
> + fprintf(
> + stderr,
> + "fmov indirect_addr,freg failed. Expected %f, got %f\n", in[1], out
> + );
> + pass = false;
> + }
> +
> + return pass;
> +}
> +
> +/* uses fmov drm,drn instruction */
> +static int test_fmov_dreg_dreg(void)
> +{
> + int pass = true;
> + double_precision_mode();
> +
> + register double out asm ("dr0") = 0.0;
> + asm volatile(
> + "mov #42,r0\n"
> + "lds r0,fpul\n"
> + "float fpul,dr2\n"
> + "fmov dr2,%0\n"
> + : "=f" (out)
> + );
> +
> + if (out != 42.0) {
> + fprintf(
> + stderr,
> + "fmov dreg,dreg failed. Expected %f, got %f\n", 42.0, out
> + );
> + pass = false;
> + }
> +
> + single_precision_mode();
> + return pass;
> +}
> +
> +/* uses fmov drm,@rn instruction */
> +static int test_fmov_dreg_addr(float *out)
> +{
> + int pass = true;
> + fill_arr(out, -1.0);
> +
> + register float *out_addr = out;
> + asm volatile(
> + "fldi0 fr0\n"
> + "fldi1 fr1\n"
> + "fschg\n" /* Set fast pairs on */
> + "fmov dr0, @%0\n"
> + "fschg\n" /* Set fast pairs off */
> + : : "r" (out_addr) : "memory"
> + );
> +
> + if (out[0] != 0.0) {
> + fprintf(
> + stderr,
> + "fmov dreg,addr failed. " \
> + "Expected element 0 to be %f, got %f\n", 0.0, out[0]
> + );
> + pass = false;
> + }
> + if (out[1] != 1.0) {
> + fprintf(
> + stderr,
> + "fmov dreg,addr failed. " \
> + "Expected element 1 to be %f, got %f\n", 1.0, out[1]
> + );
> + pass = false;
> + }
> +
> + fill_arr(out, 0.0);
> + return pass;
> +}
> +
> +/* uses fmov @rm,drn instruction */
> +static int test_fmov_addr_dreg(const float *in)
> +{
> + int pass = true;
> +
> + const register float *in_addr asm ("r1") = in;
> + register float write_out_0 asm ("fr0") = 0.0;
> + register float write_out_1 asm ("fr1") = 0.0;
> +
> + asm volatile(
> + "fschg\n" /* Set fast pairs on */
> + "fmov @%2,dr0\n"
> + "fschg\n" /* Set fast pairs off */
> + : "=f" (write_out_0), "=f" (write_out_1) : "r" (in_addr) : "memory"
> + );
> +
> + if (write_out_0 != in[0]) {
> + fprintf(
> + stderr,
> + "fmov addr,dreg failed. " \
> + "Expected element 0 to be %f, got %f\n", in[0], write_out_0
> + );
> + pass = false;
> + }
> + if (write_out_1 != in[1]) {
> + fprintf(
> + stderr,
> + "fmov addr,dreg failed. " \
> + "Expected element 1 to be %f, got %f\n", in[1], write_out_1
> + );
> + pass = false;
> + }
> +
> + return pass;
> +}
> +
> +/* uses fmov drm,@(r0,rn) instruction */
> +static int test_fmov_dreg_indirect_addr(float *out)
> +{
> + int pass = true;
> + fill_arr(out, -1.0);
> +
> + register float *out_addr = out;
> + asm volatile(
> + "mov #8,r0\n"
> + "fldi0 fr0\n"
> + "fldi1 fr1\n"
> + "fschg\n" /* Set fast pairs on */
> + "fmov dr0, @(r0, %0)\n"
> + "fschg\n" /* Set fast pairs off */
> + : : "r" (out_addr) : "memory"
> + );
> +
> + if (out[2] != 0.0) {
> + fprintf(
> + stderr,
> + "fmov dreg,indirect_addr failed. " \
> + "Expected element 0 to be %f, got %f\n", 0.0, out[2]
> + );
> + pass = false;
> + }
> +
> + if (out[3] != 1.0) {
> + fprintf(
> + stderr,
> + "fmov dreg,indirect_addr failed. " \
> + "Expected element 1 to be %f, got %f\n", 1.0, out[3]
> + );
> + pass = false;
> + }
> +
> + fill_arr(out, 0.0);
> + return pass;
> +}
> +
> +/* uses fmov @(r0,rm),drn instruction */
> +static int test_fmov_indirect_addr_dreg(const float *in)
> +{
> + int pass = true;
> +
> + const register float *in_addr asm ("r1") = in;
> + register float write_out_0 asm ("fr0") = 0.0;
> + register float write_out_1 asm ("fr1") = 0.0;
> + asm volatile(
> + "mov #8,r0\n"
> + "fschg\n" /* Set fast pairs on */
> + "fmov @(r0, %2), dr0\n"
> + "fschg\n" /* Set fast pairs off */
> + : "=f" (write_out_0), "=f" (write_out_1) : "r" (in_addr) : "memory"
> + );
> +
> + if (write_out_0 != in[2]) {
> + fprintf(
> + stderr,
> + "fmov indirect_addr,dreg failed. " \
> + "Expected element 0 to be %f, got %f\n", in[2], write_out_0
> + );
> + pass = false;
> + }
> +
> + if (write_out_1 != in[3]) {
> + fprintf(
> + stderr,
> + "fmov indirect_addr,dreg failed. " \
> + "Expected element 1 to be %f, got %f\n", in[3], write_out_1
> + );
> + pass = false;
> + }
> +
> + return pass;
> +}
> +
> +/*
> + * reads in data from `in` using fast pairs
> + * writes data to `out` using slow singles
> + * uses fmov @rm+,drn instruction
> + * uses fmov frm,@-rn instruction
> + */
> +static int test_fmov_fast_in_slow_out(const float *in, float *out)
> +{
> + int pass = true;
> + fill_arr(out, 0.0);
> +
> + const register float *in_addr = in;
> + register float *out_addr = out;
> + asm volatile(
> + "fschg\n" /* Set fast pairs on */
> + "fmov @%0+,dr0\n"
> + "fmov @%0+,dr2\n"
> + "fmov @%0+,dr4\n"
> + "fmov @%0+,dr6\n"
> + "fmov @%0+,dr8\n"
> + "fmov @%0+,dr10\n"
> + "fmov @%0+,dr12\n"
> + "fmov @%0+,dr14\n"
> + "fschg\n" /* Set fast pairs off */
> +
> + "add #64,%1\n" /* Start at the back and work to the front */
> + "fmov fr15,@-%1\n"
> + "fmov fr14,@-%1\n"
> + "fmov fr13,@-%1\n"
> + "fmov fr12,@-%1\n"
> + "fmov fr11,@-%1\n"
> + "fmov fr10,@-%1\n"
> + "fmov fr9,@-%1\n"
> + "fmov fr8,@-%1\n"
> + "fmov fr7,@-%1\n"
> + "fmov fr6,@-%1\n"
> + "fmov fr5,@-%1\n"
> + "fmov fr4,@-%1\n"
> + "fmov fr3,@-%1\n"
> + "fmov fr2,@-%1\n"
> + "fmov fr1,@-%1\n"
> + "fmov fr0,@-%1\n"
> + : "+r" (in_addr), "+r" (out_addr) :: "memory"
> + );
> +
> + for (int i = 0; i < ARR_SIZE; i++) {
> + if (in[i] != out[i]) {
> + fprintf(
> + stderr,
> + "fmov fast->slow failed. " \
> + "Expected element %i to be %f, got %f\n", i, in[i], out[i]
> + );
> + pass = false;
> + }
> + }
> +
> + fill_arr(out, 0.0);
> + return pass;
> +}
> +
> +/* reads in data from `in` using slow singles */
> +/* writes data to `out` using fast pairs */
> +/* uses fmov @rm+,frn instruction */
> +/* uses fmov drm,@-rn instruction */
> +static int test_fmov_slow_in_fast_out(const float in[16], float out[16])
> +{
> + int pass = true;
> + fill_arr(out, 0.0);
> +
> + const register float *in_addr = in;
> + register float *out_addr = out;
> + asm volatile(
> + "fmov @%0+,fr0\n"
> + "fmov @%0+,fr1\n"
> + "fmov @%0+,fr2\n"
> + "fmov @%0+,fr3\n"
> + "fmov @%0+,fr4\n"
> + "fmov @%0+,fr5\n"
> + "fmov @%0+,fr6\n"
> + "fmov @%0+,fr7\n"
> + "fmov @%0+,fr8\n"
> + "fmov @%0+,fr9\n"
> + "fmov @%0+,fr10\n"
> + "fmov @%0+,fr11\n"
> + "fmov @%0+,fr12\n"
> + "fmov @%0+,fr13\n"
> + "fmov @%0+,fr14\n"
> + "fmov @%0+,fr15\n"
> +
> + "fschg\n" /* Set fast pairs on */
> + "add #64,%1\n" /* Start at the back and work to the front */
> + "fmov dr14,@-%1\n"
> + "fmov dr12,@-%1\n"
> + "fmov dr10,@-%1\n"
> + "fmov dr8,@-%1\n"
> + "fmov dr6,@-%1\n"
> + "fmov dr4,@-%1\n"
> + "fmov dr2,@-%1\n"
> + "fmov dr0,@-%1\n"
> + "fschg\n" /* Set fast pairs off */
> + : "+r" (in_addr), "+r" (out_addr) :: "memory"
> + );
> +
> + for (int i = 0; i < ARR_SIZE; i++) {
> + if (in[i] != out[i]) {
> + fprintf(
> + stderr,
> + "fmov slow->fast failed. " \
> + "Expected element %i to be %f, got %f\n", i, in[i], out[i]
> + );
> + pass = false;
> + }
> + }
> +
> + fill_arr(out, 0.0);
> + return pass;
> +}
> +
> +int main(void)
> +{
> + single_precision_mode();
> + int pass = true;
> +
> + if (!test_fmov_freg_freg()) {
> + pass = false;
> + };
> +
> + if (!test_fmov_freg_addr(out_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_addr_freg(in_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_freg_indirect_addr(out_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_indirect_addr_freg(in_arr)) {
> + pass = false;
> + };
> +
> + if (!test_fmov_dreg_dreg()) {
> + pass = false;
> + }
> +
> + if (!test_fmov_dreg_addr(out_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_addr_dreg(in_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_dreg_indirect_addr(out_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_indirect_addr_dreg(in_arr)) {
> + pass = false;
> + };
> +
> + if (!test_fmov_fast_in_slow_out(in_arr, out_arr)) {
> + pass = false;
> + }
> +
> + if (!test_fmov_slow_in_fast_out(in_arr, out_arr)) {
> + pass = false;
> + }
> +
> + if (!pass) {
> + fprintf(
> + stderr,
> + "Fmov tests failed\n"
> + );
> + abort();
> + }
> +
> + return 0;
> +}
>
> --
> 2.43.0
>
Reviewed-by: Yoshinori Sato <[email protected] >
--
Yosinori Sato