Re: [PATCH 1/2] target/sh4: add tcg tests for fmov instructions

[email protected] Thu, 30 Jul 2026 21:24:48 +0900
Newsgroups org.nongnu.qemu-trivial,org.nongnu.qemu-devel
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