diff --git a/include/dynarmic/callbacks.h b/include/dynarmic/callbacks.h index 83203a8d..85738403 100644 --- a/include/dynarmic/callbacks.h +++ b/include/dynarmic/callbacks.h @@ -51,6 +51,10 @@ struct UserCallbacks { // This callback is called whenever a SVC instruction is executed. void (*CallSVC)(std::uint32_t swi); + // Timing-related callbacks + void (*AddTicks)(std::uint64_t ticks); + std::uint64_t (*GetTicksRemaining)(); + // Page Table // The page table is used for faster memory access. If an entry in the table is nullptr, // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. diff --git a/include/dynarmic/dynarmic.h b/include/dynarmic/dynarmic.h index 3f172ac6..bfe2ecf7 100644 --- a/include/dynarmic/dynarmic.h +++ b/include/dynarmic/dynarmic.h @@ -28,9 +28,8 @@ public: * Runs the emulated CPU for about cycle_count cycles. * Cannot be recursively called. * @param cycle_count Estimated number of cycles to run the CPU for. - * @returns Actual cycle count. */ - std::size_t Run(std::size_t cycle_count); + void Run(std::size_t cycle_count); /** * Clears the code cache of all compiled code. diff --git a/src/backend_x64/block_of_code.cpp b/src/backend_x64/block_of_code.cpp index 6db3f3bf..05d31564 100644 --- a/src/backend_x64/block_of_code.cpp +++ b/src/backend_x64/block_of_code.cpp @@ -57,13 +57,13 @@ void BlockOfCode::ClearCache() { SetCodePtr(near_code_begin); } -size_t BlockOfCode::RunCode(JitState* jit_state, size_t cycles_to_run) const { +void BlockOfCode::RunCode(JitState* jit_state, size_t cycles_to_run) const { constexpr size_t max_cycles_to_run = static_cast(std::numeric_limitscycles_remaining)>::max()); ASSERT(cycles_to_run <= max_cycles_to_run); + jit_state->cycles_to_run = cycles_to_run; jit_state->cycles_remaining = cycles_to_run; run_code(jit_state); - return cycles_to_run - jit_state->cycles_remaining; // Return number of cycles actually run. } void BlockOfCode::ReturnFromRunCode(bool MXCSR_switch) { @@ -118,6 +118,10 @@ void BlockOfCode::GenRunCode() { jg(loop); } + mov(ABI_PARAM1, qword[r15 + offsetof(JitState, cycles_to_run)]); + sub(ABI_PARAM1, qword[r15 + offsetof(JitState, cycles_remaining)]); + CallFunction(cb.AddTicks); + ABI_PopCalleeSaveRegistersAndAdjustStack(this); ret(); }; diff --git a/src/backend_x64/block_of_code.h b/src/backend_x64/block_of_code.h index e264f215..15061c19 100644 --- a/src/backend_x64/block_of_code.h +++ b/src/backend_x64/block_of_code.h @@ -30,7 +30,7 @@ public: void ClearCache(); /// Runs emulated code for approximately `cycles_to_run` cycles. - size_t RunCode(JitState* jit_state, size_t cycles_to_run) const; + void RunCode(JitState* jit_state, size_t cycles_to_run) const; /// Code emitter: Returns to dispatcher void ReturnFromRunCode(bool MXCSR_switch = true); /// Code emitter: Returns to dispatcher, forces return to host diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 223b2df4..f027417a 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -421,11 +421,21 @@ void EmitX64::EmitBXWritePC(RegAlloc& reg_alloc, IR::Block&, IR::Inst* inst) { } void EmitX64::EmitCallSupervisor(RegAlloc& reg_alloc, IR::Block&, IR::Inst* inst) { - auto args = reg_alloc.GetArgumentInfo(inst); - reg_alloc.HostCall(nullptr, args[0]); + using namespace Xbyak::util; + + reg_alloc.HostCall(nullptr); code->SwitchMxcsrOnExit(); + code->mov(code->ABI_PARAM1, qword[r15 + offsetof(JitState, cycles_to_run)]); + code->sub(code->ABI_PARAM1, qword[r15 + offsetof(JitState, cycles_remaining)]); + code->CallFunction(cb.AddTicks); + reg_alloc.EndOfAllocScope(); + auto args = reg_alloc.GetArgumentInfo(inst); + reg_alloc.HostCall(nullptr, args[0]); code->CallFunction(cb.CallSVC); + code->CallFunction(cb.GetTicksRemaining); + code->mov(qword[r15 + offsetof(JitState, cycles_to_run)], code->ABI_RETURN); + code->mov(qword[r15 + offsetof(JitState, cycles_remaining)], code->ABI_RETURN); code->SwitchMxcsrOnEntry(); } diff --git a/src/backend_x64/interface_x64.cpp b/src/backend_x64/interface_x64.cpp index 694daa4e..d9248312 100644 --- a/src/backend_x64/interface_x64.cpp +++ b/src/backend_x64/interface_x64.cpp @@ -48,8 +48,8 @@ struct Jit::Impl { std::deque invalid_cache_ranges; bool invalidate_entire_cache = false; - size_t Execute(size_t cycle_count) { - return block_of_code.RunCode(&jit_state, cycle_count); + void Execute(size_t cycle_count) { + block_of_code.RunCode(&jit_state, cycle_count); } std::string Disassemble(const IR::LocationDescriptor& descriptor) { @@ -155,18 +155,16 @@ Jit::Jit(UserCallbacks callbacks) : impl(std::make_unique(this, callbacks) Jit::~Jit() {} -size_t Jit::Run(size_t cycle_count) { +void Jit::Run(size_t cycle_count) { ASSERT(!is_executing); is_executing = true; SCOPE_EXIT({ this->is_executing = false; }); impl->jit_state.halt_requested = false; - size_t cycles_executed = impl->Execute(cycle_count); + impl->Execute(cycle_count); impl->PerformCacheInvalidation(); - - return cycles_executed; } void Jit::ClearCache() { diff --git a/src/backend_x64/jitstate.h b/src/backend_x64/jitstate.h index 2e62b9c3..a809e794 100644 --- a/src/backend_x64/jitstate.h +++ b/src/backend_x64/jitstate.h @@ -36,6 +36,7 @@ struct JitState { // For internal use (See: BlockOfCode::RunCode) u32 guest_MXCSR = 0x00001f80; u32 save_host_MXCSR = 0; + s64 cycles_to_run = 0; s64 cycles_remaining = 0; bool halt_requested = false; diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index b40aa74a..c8eefece 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -123,6 +123,8 @@ static void Fail() { FAIL(); } +static void AddTicks(u64) {} + static Dynarmic::UserCallbacks GetUserCallbacks() { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.InterpreterFallback = &InterpreterFallback; @@ -137,6 +139,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks() { user_callbacks.memory.Write16 = &MemoryWrite16; user_callbacks.memory.Write32 = &MemoryWrite32; user_callbacks.memory.Write64 = &MemoryWrite64; + user_callbacks.AddTicks = &AddTicks; return user_callbacks; } diff --git a/tests/arm/fuzz_thumb.cpp b/tests/arm/fuzz_thumb.cpp index e1287c10..1fec6b2c 100644 --- a/tests/arm/fuzz_thumb.cpp +++ b/tests/arm/fuzz_thumb.cpp @@ -114,6 +114,8 @@ static void Fail() { FAIL(); } +static void AddTicks(u64) {} + static Dynarmic::UserCallbacks GetUserCallbacks() { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.InterpreterFallback = &InterpreterFallback; @@ -128,6 +130,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks() { user_callbacks.memory.Write16 = &MemoryWrite16; user_callbacks.memory.Write32 = &MemoryWrite32; user_callbacks.memory.Write64 = &MemoryWrite64; + user_callbacks.AddTicks = &AddTicks; return user_callbacks; } diff --git a/tests/arm/test_thumb_instructions.cpp b/tests/arm/test_thumb_instructions.cpp index 07fdcbd4..64375b42 100644 --- a/tests/arm/test_thumb_instructions.cpp +++ b/tests/arm/test_thumb_instructions.cpp @@ -46,11 +46,14 @@ static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*) { jit->Cpsr() = interp_state.Cpsr; } +static void AddTicks(u64) {} + static Dynarmic::UserCallbacks GetUserCallbacks() { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.memory.Read32 = &MemoryRead32; user_callbacks.memory.ReadCode = &MemoryReadCode; user_callbacks.InterpreterFallback = &InterpreterFallback; + user_callbacks.AddTicks = &AddTicks; return user_callbacks; }