Richard Henderson <richard.henderson@linaro.org> 于2025年9月23日周二 10:10写道:
>
> On 9/22/25 19:04, 李威威 wrote:
> >  > If there's a problem with 1 tb, there's also a problem with 2 tb like
> >  >
> >  >         jal     zero, #4
> >  >         jal     zero, #-4
> >  >
> >
> > I tried this case. And it didn't have this problem.
> > This problem seems only existed in single tb loop.
> >
> >  >
> >  > But unlinking the tb should be part of invalidation, so I don't quite see where the
> >  > problem is.  You need to expand on the description of the problem.
> >  >
> >
> > I think the problem is the single tb is always in use  when the single tb is linked with
> > itself,
> > and it cannot be updated when we update the code。
>
> There's no use count for tb's, so that explanation doesn't make sense.
> Can you please share a testcase for this?

I think the problem is here:
/*
 * Invalidate all TBs which intersect with the target address range.
 * Called with mmap_lock held for user-mode emulation.
 * NOTE: this function must not be called while a TB is running.
 */
void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
                              tb_page_addr_t last)
{
    TranslationBlock *tb;
    PageForEachNext n;

    assert_memory_lock();

    PAGE_FOR_EACH_TB(start, last, unused, tb, n) {
        tb_phys_invalidate__locked(tb);
    }
}

Only un-running tb will be invalidated.

This problem is found after a recent change in V8. Following is an example built by Kasper Land:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h> // For mmap, mprotect, munmap.
#include <pthread.h>  // For pthread functions.
#include <stdint.h>   // For intptr_t, uint32_t.
#include <unistd.h>   // For sleep.

// This program is specific to the RISC-V architecture.
#if !defined(__riscv)
#error "This code is intended for RISC-V architectures only."
#endif

// Define a function pointer type that takes no arguments and returns an int.
typedef int (*jit_func)();

/**
 * @brief The function that will be executed by the new thread.
 * * @param arg A pointer to the JIT-compiled function.
 * @return void* The integer result from the JIT function, cast to a void pointer.
 */
void* thread_runner(void* arg) {
    // Cast the argument back to our function pointer type.
    jit_func func = (jit_func)arg;

    printf("  [Thread] Executing JIT'ed code...\n");
    int result = func();
    printf("  [Thread] JIT'ed code returned: %d\n", result);

    // To return a simple integer value from a thread, it's safe to cast it
    // through intptr_t, which is an integer type guaranteed to be able to hold a pointer.
    return (void*)(intptr_t)result;
}

int main() {
    // ## 1. RISC-V machine code.
    // Assembly:
    //   L: j L          ; Jump to self (spin).
    //   li a0, 42       ; Place 42 into the return value register a0.
    //   ret             ; Return to caller.
    uint32_t machine_code[] = {
        0x0000006f, // jal zero, #0
        0x02a00513, // addi a0, zero, 42
        0x00008067  // jalr zero, ra, 0
    };
    size_t code_size = sizeof(machine_code);

    // ## 2. Allocate executable memory.
    void* buffer = mmap(
        NULL,
        code_size,
        PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_PRIVATE | MAP_ANONYMOUS,
        -1, 0
    );

    if (buffer == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    // ## 3. Copy machine code into buffer.
    memcpy(buffer, machine_code, code_size);
    printf("[Main] Successfully generated machine code at address: %p\n", buffer);

    // ## 4. Execute the code in a separate thread.
    pthread_t thread_id;
    void* thread_return_value;

    // The JIT'ed code is now ready to be run.
    jit_func func_to_run = (jit_func)buffer;

    printf("[Main] Creating a new thread to run the JIT code...\n");
    if (pthread_create(&thread_id, NULL, thread_runner, func_to_run) != 0) {
        perror("pthread_create failed");
        munmap(buffer, code_size);
        return 1;
    }

    // Wait a second and then try to patch the generated code
    // to get the runner thread to get unstuck by patching the
    // spin jump.
    sleep(1);
    printf("[Main] Patching spin jump to nop...\n");
    uint32_t* patchable = (uint32_t*) buffer;
    patchable[0] = 0x00000013;  // nop

    printf("[Main] Flush icache...\n");
    __builtin___clear_cache((char*) patchable, (char*) &patchable[1]);

    printf("[Main] Waiting for the thread to finish...\n");
    if (pthread_join(thread_id, &thread_return_value) != 0) {
        perror("pthread_join failed");
        munmap(buffer, code_size);
        return 1;
    }

    int result = (int)(intptr_t)thread_return_value;
    printf("[Main] The JIT'ed function (run in a thread) returned: %d\n", result);
    printf("[Main] Execution successful!\n");

    // ## 6. Clean Up.
    munmap(buffer, code_size);

    return 0;
}


>
> For extra kudos, a small assembly test for tests/tcg/loongarch64/system/, so that we have
> a regression test for the issue.  :-)
>
>
> r~