Skip to content

[RV64_DYNAREC] Fix the issue where source register values get overwritten.#4032

Merged
ptitSeb merged 1 commit into
ptitSeb:mainfrom
zengdage:fastnan
Jul 13, 2026
Merged

[RV64_DYNAREC] Fix the issue where source register values get overwritten.#4032
ptitSeb merged 1 commit into
ptitSeb:mainfrom
zengdage:fastnan

Conversation

@zengdage

Copy link
Copy Markdown
Contributor

In the implementations of addsd/subsd/mulsd/divsd when BOX64_DYNAREC_FASTNAN is enabled, the source register values are being overwritten. This violates the x86-64 instruction specification, which prohibits modification of source registers. The resulting loss of source register data triggers subsequent runtime errors.

I referenced the LA64 and ARM64 implementations and used temporary floating-point registers to store intermediate values.

SNAN-to-QNAN conversion handling may also need to be added here. I will address this in a separate follow-up patch.

In the implementations of `addsd/subsd/mulsd/divsd` when BOX64_DYNAREC_FASTNAN
is enabled, the source register values are being overwritten. This violates the
x86-64 instruction specification, which prohibits modification of source
registers. The resulting loss of source register data triggers subsequent
runtime errors.

I referenced the LA64 and ARM64 implementations and used temporary floating-point
registers to store intermediate values.

SNAN-to-QNAN conversion handling may also need to be added here. I will address
this in a separate follow-up patch.
@zengdage

Copy link
Copy Markdown
Contributor Author

Tests as follow:

cimpile command: gcc -msse2 -O2 -o test test.c

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>

typedef union {
    uint64_t u64[2];
    double   d64[2];
    __attribute__((aligned(16))) char align[16];
} xmm128_t;

static void print_xmm(const char* name, const xmm128_t* v) {
    printf("%s = [hi:0x%016"PRIx64", lo:0x%016"PRIx64"]\n", name, v->u64[1], v->u64[0]);
}

void test_mulsd(int *pass)
{
    printf("\n=== Test 1: MULSD xmm0, xmm1 ===\n");

    xmm128_t dst = {{ 0x4000000000000000ULL,
                      0x1234567890ABCDEFULL }};
    xmm128_t src = {{ 0x4008000000000000ULL,
                      0xDEADBEEFCAFEBABEULL }};
    xmm128_t dst_out, src_out;

    print_xmm("dst before", &dst);
    print_xmm("src before", &src);

    __asm__ __volatile__ (
        "movaps %[dst], %%xmm0\n\t"
        "movaps %[src], %%xmm1\n\t"
        "mulsd  %%xmm1, %%xmm0\n\t"
        "movaps %%xmm0, %[dst_out]\n\t"
        "movaps %%xmm1, %[src_out]\n\t"
        : [dst_out] "=m" (dst_out), [src_out] "=m" (src_out)
        : [dst] "m" (dst), [src] "m" (src)
        : "xmm0", "xmm1"
    );

    print_xmm("dst after ", &dst_out);
    print_xmm("src after ", &src_out);

    if (src_out.u64[0] != 0x4008000000000000ULL) {
        printf("FAIL: src lo = 0x%016"PRIx64", expected 0x4008000000000000 (3.0)\n", src_out.u64[0]);
        *pass = 0;
    }
    if (src_out.u64[1] != 0xDEADBEEFCAFEBABEULL) {
        printf("FAIL: src hi = 0x%016"PRIx64", expected 0xDEADBEEFCAFEBABE\n", src_out.u64[1]);
        *pass = 0;
    }
}


void test_addsd(int *pass)
{
    printf("\n=== Test 1: ADDSD xmm0, xmm1 ===\n");

    xmm128_t dst = {{ 0x4000000000000000ULL,
                      0x1234567890ABCDEFULL }};
    xmm128_t src = {{ 0x4008000000000000ULL,
                      0xDEADBEEFCAFEBABEULL }};
    xmm128_t dst_out, src_out;

    print_xmm("dst before", &dst);
    print_xmm("src before", &src);

    __asm__ __volatile__ (
        "movaps %[dst], %%xmm0\n\t"
        "movaps %[src], %%xmm1\n\t"
        "addsd  %%xmm1, %%xmm0\n\t"
        "movaps %%xmm0, %[dst_out]\n\t"
        "movaps %%xmm1, %[src_out]\n\t"
        : [dst_out] "=m" (dst_out), [src_out] "=m" (src_out)
        : [dst] "m" (dst), [src] "m" (src)
        : "xmm0", "xmm1"
    );

    print_xmm("dst after ", &dst_out);
    print_xmm("src after ", &src_out);

    if (src_out.u64[0] != 0x4008000000000000ULL) {
        printf("FAIL: src lo = 0x%016"PRIx64", expected 0x4008000000000000 (3.0)\n", src_out.u64[0]);
        *pass = 0;
    }
    if (src_out.u64[1] != 0xDEADBEEFCAFEBABEULL) {
        printf("FAIL: src hi = 0x%016"PRIx64", expected 0xDEADBEEFCAFEBABE\n", src_out.u64[1]);
        *pass = 0;
    }
}

void test_subsd(int *pass)
{
    printf("\n=== Test 1: SUBSD xmm0, xmm1 ===\n");

    xmm128_t dst = {{ 0x4000000000000000ULL,
                      0x1234567890ABCDEFULL }};
    xmm128_t src = {{ 0x4008000000000000ULL,
                      0xDEADBEEFCAFEBABEULL }};
    xmm128_t dst_out, src_out;

    print_xmm("dst before", &dst);
    print_xmm("src before", &src);

    __asm__ __volatile__ (
        "movaps %[dst], %%xmm0\n\t"
        "movaps %[src], %%xmm1\n\t"
        "subsd  %%xmm1, %%xmm0\n\t"
        "movaps %%xmm0, %[dst_out]\n\t"
        "movaps %%xmm1, %[src_out]\n\t"
        : [dst_out] "=m" (dst_out), [src_out] "=m" (src_out)
        : [dst] "m" (dst), [src] "m" (src)
        : "xmm0", "xmm1"
    );

    print_xmm("dst after ", &dst_out);
    print_xmm("src after ", &src_out);

    if (src_out.u64[0] != 0x4008000000000000ULL) {
        printf("FAIL: src lo = 0x%016"PRIx64", expected 0x4008000000000000 (3.0)\n", src_out.u64[0]);
        *pass = 0;
    }
    if (src_out.u64[1] != 0xDEADBEEFCAFEBABEULL) {
        printf("FAIL: src hi = 0x%016"PRIx64", expected 0xDEADBEEFCAFEBABE\n", src_out.u64[1]);
        *pass = 0;
    }
}

void test_divsd(int *pass)
{
    printf("\n=== Test 1: DIVSD xmm0, xmm1 ===\n");

    xmm128_t dst = {{ 0x4000000000000000ULL,
                      0x1234567890ABCDEFULL }};
    xmm128_t src = {{ 0x4008000000000000ULL,
                      0xDEADBEEFCAFEBABEULL }};
    xmm128_t dst_out, src_out;

    print_xmm("dst before", &dst);
    print_xmm("src before", &src);

    __asm__ __volatile__ (
        "movaps %[dst], %%xmm0\n\t"
        "movaps %[src], %%xmm1\n\t"
        "divsd  %%xmm1, %%xmm0\n\t"
        "movaps %%xmm0, %[dst_out]\n\t"
        "movaps %%xmm1, %[src_out]\n\t"
        : [dst_out] "=m" (dst_out), [src_out] "=m" (src_out)
        : [dst] "m" (dst), [src] "m" (src)
        : "xmm0", "xmm1"
    );

    print_xmm("dst after ", &dst_out);
    print_xmm("src after ", &src_out);

    if (src_out.u64[0] != 0x4008000000000000ULL) {
        printf("FAIL: src lo = 0x%016"PRIx64", expected 0x4008000000000000 (3.0)\n", src_out.u64[0]);
        *pass = 0;
    }
    if (src_out.u64[1] != 0xDEADBEEFCAFEBABEULL) {
        printf("FAIL: src hi = 0x%016"PRIx64", expected 0xDEADBEEFCAFEBABE\n", src_out.u64[1]);
        *pass = 0;
    }
}

int main() {
    int pass = 1;

    test_addsd(&pass);
    test_subsd(&pass);
    test_mulsd(&pass);
    test_divsd(&pass);

    printf("\n%s\n", pass ? "ALL PASSED" : "SOME TESTS FAILED");
    return pass ? 0 : 1;
}

@ksco ksco left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ksco ksco changed the title [DYNAREC] Fix the issue where source register values get overwritten. [RV64_DYNAREC] Fix the issue where source register values get overwritten. Jul 13, 2026
@ptitSeb ptitSeb merged commit 0ccd751 into ptitSeb:main Jul 13, 2026
28 checks passed
@zengdage zengdage deleted the fastnan branch July 13, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants