cpu: move halt instruction block to KernelState, shared across all threads

Each thread was allocating its own identical 4-byte NOP+WFI block.
Since all threads point LR at the same sentinel, one allocation in KernelState is sufficient.

Note that this is a Dynarmic-specific "hack" to make it stop execution (`jit->Run()`) after the guest code returns (jumps back LR):
- Prior JIT execution, we prepare LR to point to this NOP+WFI block.
- When the guest function returns, it will jump into this NOP+WFI block and `Dynarmic::A32::Exception::WaitForInterrupt` will be called. Then we call `cpu->jit->HaltExecution()` which will make `jit->Run()` return
This commit is contained in:
Sergi (セルジ)
2026-04-22 19:43:43 +09:00
committed by Zangetsu
parent 9abe85f90f
commit 6e52f1ce0c
5 changed files with 15 additions and 14 deletions
-4
View File
@@ -19,7 +19,6 @@
#include <cpu/common.h>
#include <cpu/disasm/state.h>
#include <mem/block.h>
#include <mem/state.h>
#include <util/types.h>
@@ -32,9 +31,6 @@ struct CPUState {
MemState *mem = nullptr;
DisasmState disasm;
Block halt_instruction;
Address halt_instruction_pc; // thumb mode pc
CPUInterfacePtr cpu;
bool svc_called;
uint32_t svc;
-8
View File
@@ -43,14 +43,6 @@ CPUStatePtr init_cpu(bool cpu_opt, SceUID thread_id, std::size_t processor_id, M
state->mem = &mem;
state->thread_id = thread_id;
// TODO: we can move this to kernel after we drop unicorn
// unicorn is unable to detect whether the exit was because of halt or not
state->halt_instruction = alloc_block(mem, 4, "halt_instruction");
const auto halt_ptr = state->halt_instruction.get_ptr<uint16_t>();
*halt_ptr.get(mem) = 0xBF00; // NOP
*(halt_ptr.get(mem) + 1) = 0xBF30; // WFI
state->halt_instruction_pc = state->halt_instruction.get() | 1;
if (!init(state->disasm)) {
return CPUStatePtr();
}
+5
View File
@@ -24,6 +24,7 @@
#include <kernel/sync_primitives.h>
#include <kernel/types.h>
#include <mem/allocator.h>
#include <mem/block.h>
#include <mem/ptr.h>
#include <mem/util.h>
#include <rtc/rtc.h>
@@ -136,6 +137,10 @@ struct KernelState {
CorenumAllocator corenum_allocator;
CallImportFunc call_import;
// Shared NOP+WFI sentinel used by the Dynarmic as the halt return address
Block halt_instruction;
Address halt_instruction_pc;
ObjectStore obj_store;
uint64_t start_tick;
+8
View File
@@ -21,6 +21,7 @@
#include <cpu/common.h>
#include <kernel/state.h>
#include <mem/functions.h>
#include <kernel/thread/thread_state.h>
@@ -90,6 +91,13 @@ bool KernelState::init(MemState &mem, const CallImportFunc &call_import, bool cp
this->call_import = call_import;
this->cpu_opt = cpu_opt;
// Generate halt instruction (NOP + WFI)
halt_instruction = alloc_block(mem, 4, "halt_instruction");
const auto halt_ptr = halt_instruction.get_ptr<uint16_t>().get(mem);
halt_ptr[0] = 0xBF00; // NOP
halt_ptr[1] = 0xBF30; // WFI
halt_instruction_pc = halt_instruction.get() | 1; // thumb mode pc
return true;
}
+2 -2
View File
@@ -138,7 +138,7 @@ int ThreadState::start(SceSize arglen, const Ptr<void> argp, bool run_entry_call
call_level = 1;
load_context(*cpu, init_cpu_ctx);
write_pc(*cpu, entry_point);
write_lr(*cpu, cpu->halt_instruction_pc);
write_lr(*cpu, kernel.halt_instruction_pc);
write_reg(*cpu, 0, arglen);
// Copy data to stack
@@ -365,7 +365,7 @@ uint32_t ThreadState::run_callback(Address callback_address, const std::vector<u
call_level++;
// we shouldn't have to clean the context I believe
write_pc(*cpu, callback_address);
write_lr(*cpu, cpu->halt_instruction_pc);
write_lr(*cpu, kernel.halt_instruction_pc);
push_arguments(args);
thread_lock.unlock();