[A64] Fix SET_NJM to update cached fpcr_vmx

This commit is contained in:
Herman S.
2026-03-25 08:51:42 +09:00
parent c4f4412df9
commit ef13662234
2 changed files with 146 additions and 13 deletions

View File

@@ -537,6 +537,115 @@ TEST_CASE("NJM_DEFAULT_ON", "[backend]") {
memory->SystemHeapFree(stack_address);
}
// =============================================================================
// SET_NJM — verify NJM toggle updates backend context correctly
// =============================================================================
// NJM (Non-Java Mode) is a VMX feature (VSCR bit 16) that controls
// flush-to-zero for vector FP operations. We verify that SET_NJM
// correctly updates the cached VMX FPCR/MXCSR and the NJM flag.
static uint32_t observed_njm_flags_after_set = 0;
static uint32_t observed_vmx_fpcr_after_set = 0;
static void ReadBackendNJMState(ppc::PPCContext* ctx, void* arg0, void* arg1) {
#if XE_ARCH_AMD64
auto* bctx = reinterpret_cast<xe::cpu::backend::x64::X64BackendContext*>(
reinterpret_cast<intptr_t>(ctx) -
sizeof(xe::cpu::backend::x64::X64BackendContext));
observed_njm_flags_after_set = bctx->flags;
observed_vmx_fpcr_after_set = bctx->mxcsr_vmx;
#elif XE_ARCH_ARM64
auto* bctx = reinterpret_cast<xe::cpu::backend::a64::A64BackendContext*>(
reinterpret_cast<intptr_t>(ctx) -
sizeof(xe::cpu::backend::a64::A64BackendContext));
observed_njm_flags_after_set = bctx->flags;
observed_vmx_fpcr_after_set = bctx->fpcr_vmx;
#endif
}
// Helper to build and run a SET_NJM test function.
static void RunSetNJMTest(int njm_value) {
observed_njm_flags_after_set = 0;
observed_vmx_fpcr_after_set = 0;
auto memory = std::make_unique<Memory>();
memory->Initialize();
std::unique_ptr<xe::cpu::backend::Backend> backend;
#if XE_ARCH_AMD64
backend.reset(new xe::cpu::backend::x64::X64Backend());
#elif XE_ARCH_ARM64
backend.reset(new xe::cpu::backend::a64::A64Backend());
#endif
REQUIRE(backend);
auto processor = std::make_unique<Processor>(memory.get(), nullptr);
processor->Setup(std::move(backend));
auto* builtin_fn = processor->DefineBuiltin(
"ReadBackendNJMState", ReadBackendNJMState, nullptr, nullptr);
auto module = std::make_unique<TestModule>(
processor.get(), "Test",
[](uint32_t address) { return address == 0x80000000; },
[builtin_fn, njm_value](HIRBuilder& b) {
b.SetNJM(b.LoadConstantInt8(njm_value ? 1 : 0));
b.CallExtern(builtin_fn);
b.Return();
return true;
},
/*skip_cf_simplification=*/true);
processor->AddModule(std::move(module));
processor->backend()->CommitExecutableRange(0x80000000, 0x80010000);
auto fn = processor->ResolveFunction(0x80000000);
REQUIRE(fn != nullptr);
uint32_t stack_size = 64 * 1024;
uint32_t stack_address = memory->SystemHeapAlloc(stack_size);
auto thread_state = std::make_unique<ThreadState>(processor.get(), 0x100,
stack_address + stack_size);
auto ctx = thread_state->context();
ctx->lr = 0xBCBCBCBC;
fn->Call(thread_state.get(), uint32_t(ctx->lr));
memory->SystemHeapFree(stack_address);
}
TEST_CASE("SET_NJM_ON", "[backend]") {
RunSetNJMTest(1);
// NJM flag should be set.
#if XE_ARCH_AMD64
REQUIRE((observed_njm_flags_after_set &
(1U << xe::cpu::backend::x64::kX64BackendNJMOn)) != 0);
// MXCSR should have FZ and DAZ set.
REQUIRE((observed_vmx_fpcr_after_set & (1 << 15)) != 0); // FZ
REQUIRE((observed_vmx_fpcr_after_set & (1 << 6)) != 0); // DAZ
#elif XE_ARCH_ARM64
REQUIRE((observed_njm_flags_after_set &
(1U << xe::cpu::backend::a64::kA64BackendNJMOn)) != 0);
// FPCR_VMX should have FZ (bit 24) set.
REQUIRE((observed_vmx_fpcr_after_set & (1 << 24)) != 0);
#endif
}
TEST_CASE("SET_NJM_OFF", "[backend]") {
RunSetNJMTest(0);
// NJM flag should be cleared.
#if XE_ARCH_AMD64
REQUIRE((observed_njm_flags_after_set &
(1U << xe::cpu::backend::x64::kX64BackendNJMOn)) == 0);
// MXCSR should NOT have FZ or DAZ.
REQUIRE((observed_vmx_fpcr_after_set & (1 << 15)) == 0);
REQUIRE((observed_vmx_fpcr_after_set & (1 << 6)) == 0);
#elif XE_ARCH_ARM64
REQUIRE((observed_njm_flags_after_set &
(1U << xe::cpu::backend::a64::kA64BackendNJMOn)) == 0);
// FPCR_VMX should NOT have FZ (bit 24).
REQUIRE((observed_vmx_fpcr_after_set & (1 << 24)) == 0);
#endif
}
// =============================================================================
// Atomic Exchange I32
// =============================================================================