Skip to content

[RV64_DYNAREC] Keep live XMMs when reflecting cache - #4624

Open
zengdage wants to merge 1 commit into
ptitSeb:mainfrom
zengdage:BOX64_DYNAREC_TEST
Open

zengdage wants to merge 1 commit into
ptitSeb:mainfrom
zengdage:BOX64_DYNAREC_TEST

Conversation

@zengdage

Copy link
Copy Markdown
Contributor

When BOX64_DYNAREC_TEST is enabled, dead=1 will corrupt ymm register value.

@ptitSeb

ptitSeb commented Oct 29, 2026

Copy link
Copy Markdown
Owner

When BOX64_DYNAREC_TEST is enabled, dead=1 will corrupt ymm register value.

But is that a BOX64_DYNAREC_TEST specific issue? is it ok when dynarec_test is not enabled?

@zengdage

Copy link
Copy Markdown
Contributor Author

When BOX64_DYNAREC_TEST is enabled, dead=1 will corrupt ymm register value.

But is that a BOX64_DYNAREC_TEST specific issue? is it ok when dynarec_test is not enabled?

Yes. This function is only called when either BOX64_DYNAREC_TEST or BOX64_DYNAREC_TRACE is enabled.

In my tests, enabling BOX64_DYNAREC_TEST triggers an error, while BOX64_DYNAREC_TRACE works fine. Therefore, this does not affect normal usage. I just put forward this patch while I was at it.

@zengdage

zengdage commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

When BOX64_DYNAREC_TEST is enabled, dead=1 will corrupt ymm register value.

But is that a BOX64_DYNAREC_TEST specific issue? is it ok when dynarec_test is not enabled?

Yes. This function is only called when either BOX64_DYNAREC_TEST or BOX64_DYNAREC_TRACE is enabled.

In my tests, enabling BOX64_DYNAREC_TEST triggers an error, while BOX64_DYNAREC_TRACE works fine. Therefore, this does not affect normal usage. I just put forward this patch while I was at it.

Update: It also triggers an error when BOX64_DYNAREC_TRACE=1 and BOX64_TRACE=1 are enabled.

// Build with: gcc -O2 -Wall -Wextra -mavx -o test test.c

#include <stdio.h>

typedef struct __attribute__((aligned(32))) {
    float v[8];
} f32x8;

static int check_f32(const char* name, const f32x8* got, const f32x8* expected)
{
    for (int i = 0; i < 8; ++i)
        if (got->v[i] != expected->v[i]) {
            printf("%s lane %d: got %.9g, expected %.9g\n", name, i, got->v[i], expected->v[i]);
            return 1;
        }
    return 0;
}

static int test_alias_ps(void)
{
    const f32x8 a = {{1.0f, 2.0f, 3.0f, 4.0f, 100.0f, 200.0f, 300.0f, 400.0f}};
    const f32x8 b = {{10.0f, 20.0f, 30.0f, 40.0f, 1000.0f, 2000.0f, 3000.0f, 4000.0f}};
    const f32x8 expected_src1 = {{-9.0f, -18.0f, -27.0f, -36.0f,
                                  1000.0f, 2000.0f, 3000.0f, 4000.0f}};
    const f32x8 expected_src2 = {{11.0f, 22.0f, 33.0f, 44.0f,
                                  1000.0f, 2000.0f, 3000.0f, 4000.0f}};
    f32x8 got_src1, got_src2;

    __asm__ volatile(
        "vmovdqu %1, %%ymm0\n\t"
        "vmovdqu %2, %%ymm1\n\t"
        "vsubps %%xmm1, %%xmm0, %%xmm0\n\t"
        "vsubps %%xmm1, %%xmm0, %%xmm0\n\t"
        "vaddps %%ymm1, %%ymm0, %%ymm0\n\t"
        "vmovdqu %%ymm0, %0"
        : "=m"(got_src1)
        : "m"(a), "m"(b)
        : "ymm0", "ymm1", "memory");

    __asm__ volatile(
        "vmovdqu %1, %%ymm0\n\t"
        "vmovdqu %2, %%ymm1\n\t"
        "vsubps %%xmm0, %%xmm1, %%xmm0\n\t"
        "vsubps %%xmm0, %%xmm1, %%xmm0\n\t"
        "vaddps %%ymm1, %%ymm0, %%ymm0\n\t"
        "vmovdqu %%ymm0, %0"
        : "=m"(got_src2)
        : "m"(a), "m"(b)
        : "ymm0", "ymm1", "memory");

    return check_f32("f32 destination aliases src1", &got_src1, &expected_src1)
        + check_f32("f32 destination aliases src2", &got_src2, &expected_src2);
}

int main(void)
{
    int failures = test_alias_ps();
    printf("AVX XMM/YMM cache transition: %d checks, %d failures\n", 2, failures);
    return failures != 0;
}

@ptitSeb
ptitSeb requested a review from ksco October 29, 2026 08:10
@ptitSeb

ptitSeb commented Oct 29, 2026

Copy link
Copy Markdown
Owner

But my question is: without dynarec_test or trace enabled, is the old behaviour correct? And if yes, why the new code isn't only for when dynarec test is enabled?

@zengdage

Copy link
Copy Markdown
Contributor Author

But my question is: without dynarec_test or trace enabled, is the old behaviour correct? And if yes, why the new code isn't only for when dynarec test is enabled?

I think there is a issue in the RV64 sse_reflectcache implementation, and this will need to be judged by ksco.

My analysis is: after sse_reflectcache, the lower-half XMM value of the vector register is assumed to remain valid, but in reality it has been overwritten by the YMM after the vslidedown instruction. This stale XMM value is then used later, which explains the failures when dynarec_test is enabled.

@zengdage zengdage changed the title [RV64_DYNAREC] Keep live YMMs when reflecting cache [RV64_DYNAREC] Keep live XMMs when reflecting cache Oct 29, 2026
When BOX64_DYNAREC_TEST is enabled, `dead=1` will corrupt xmm register value.
@ksco

ksco commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

I'll review this later today.

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