aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMerry <[email protected]>2024-02-10 19:14:01 +0000
committerMerry <[email protected]>2024-02-10 19:31:07 +0000
commitee84ec0bb9337bc04888b8960e3464b79524eefe (patch)
tree6265a936f57e7e01145bf35c3442e967abe3cb43
parent6d0995c948bbaa044824e725841f93e5a4337a1d (diff)
downloaddynarmic-ee84ec0bb9337bc04888b8960e3464b79524eefe.tar.gz
dynarmic-ee84ec0bb9337bc04888b8960e3464b79524eefe.zip
backend/x64: Reduce races on invalidation requests in interface
This situation occurs when RequestCacheInvalidation is called from multiple threads. This results in unusual issues around memory allocation which arise from concurrent access to invalid_cache_ranges. There are several reasons for this: 1. No locking around the invalidation queue. 2. is_executing is not multithread safe. So here we reduce any cache clear or any invalidation to raise a CacheInvalidation halt, which we execute immediately before or immediately after Run() instead.
-rw-r--r--src/dynarmic/backend/x64/a32_interface.cpp227
-rw-r--r--src/dynarmic/backend/x64/a64_interface.cpp54
2 files changed, 163 insertions, 118 deletions
diff --git a/src/dynarmic/backend/x64/a32_interface.cpp b/src/dynarmic/backend/x64/a32_interface.cpp
index 2c6b22f9..84f84f5d 100644
--- a/src/dynarmic/backend/x64/a32_interface.cpp
+++ b/src/dynarmic/backend/x64/a32_interface.cpp
@@ -5,6 +5,7 @@
#include <functional>
#include <memory>
+#include <mutex>
#include <boost/icl/interval_set.hpp>
#include <fmt/format.h>
@@ -66,18 +67,17 @@ struct Jit::Impl {
, conf(std::move(conf))
, jit_interface(jit) {}
- A32JitState jit_state;
- BlockOfCode block_of_code;
- A32EmitX64 emitter;
- Optimization::PolyfillOptions polyfill_options;
+ ~Impl() = default;
- const A32::UserConfig conf;
+ HaltReason Run() {
+ ASSERT(!jit_interface->is_executing);
+ PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
- // Requests made during execution to invalidate the cache are queued up here.
- boost::icl::interval_set<u32> invalid_cache_ranges;
- bool invalidate_entire_cache = false;
+ jit_interface->is_executing = true;
+ SCOPE_EXIT {
+ jit_interface->is_executing = false;
+ };
- HaltReason Execute() {
const CodePtr current_codeptr = [this] {
// RSB optimization
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
@@ -89,11 +89,44 @@ struct Jit::Impl {
return GetCurrentBlock();
}();
- return block_of_code.RunCode(&jit_state, current_codeptr);
+ const HaltReason hr = block_of_code.RunCode(&jit_state, current_codeptr);
+
+ PerformRequestedCacheInvalidation(hr);
+
+ return hr;
}
HaltReason Step() {
- return block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
+ ASSERT(!jit_interface->is_executing);
+ PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
+
+ jit_interface->is_executing = true;
+ SCOPE_EXIT {
+ jit_interface->is_executing = false;
+ };
+
+ const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
+
+ PerformRequestedCacheInvalidation(hr);
+
+ return hr;
+ }
+
+ void ClearCache() {
+ std::unique_lock lock{invalidation_mutex};
+ invalidate_entire_cache = true;
+ HaltExecution(HaltReason::CacheInvalidation);
+ }
+
+ void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
+ std::unique_lock lock{invalidation_mutex};
+ invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
+ HaltExecution(HaltReason::CacheInvalidation);
+ }
+
+ void Reset() {
+ ASSERT(!jit_interface->is_executing);
+ jit_state = {};
}
void HaltExecution(HaltReason hr) {
@@ -108,38 +141,49 @@ struct Jit::Impl {
jit_state.exclusive_state = 0;
}
- void PerformCacheInvalidation() {
- if (invalidate_entire_cache) {
- jit_state.ResetRSB();
- block_of_code.ClearCache();
- emitter.ClearCache();
+ std::array<u32, 16>& Regs() {
+ return jit_state.Reg;
+ }
- invalid_cache_ranges.clear();
- invalidate_entire_cache = false;
- return;
- }
+ const std::array<u32, 16>& Regs() const {
+ return jit_state.Reg;
+ }
- if (invalid_cache_ranges.empty()) {
- return;
- }
+ std::array<u32, 64>& ExtRegs() {
+ return jit_state.ExtReg;
+ }
- jit_state.ResetRSB();
- emitter.InvalidateCacheRanges(invalid_cache_ranges);
- invalid_cache_ranges.clear();
+ const std::array<u32, 64>& ExtRegs() const {
+ return jit_state.ExtReg;
}
- void RequestCacheInvalidation() {
- if (jit_interface->is_executing) {
- HaltExecution(HaltReason::CacheInvalidation);
- return;
- }
+ u32 Cpsr() const {
+ return jit_state.Cpsr();
+ }
- PerformCacheInvalidation();
+ void SetCpsr(u32 value) {
+ return jit_state.SetCpsr(value);
}
-private:
- Jit* jit_interface;
+ u32 Fpscr() const {
+ return jit_state.Fpscr();
+ }
+
+ void SetFpscr(u32 value) {
+ return jit_state.SetFpscr(value);
+ }
+
+ void DumpDisassembly() const {
+ const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
+ Common::DumpDisassembledX64(block_of_code.GetCodeBegin(), size);
+ }
+
+ std::vector<std::string> Disassemble() const {
+ const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
+ return Common::DisassembleX64(block_of_code.GetCodeBegin(), size);
+ }
+private:
static CodePtr GetCurrentBlockThunk(void* this_voidptr) {
Jit::Impl& this_ = *static_cast<Jit::Impl*>(this_voidptr);
return this_.GetCurrentBlock();
@@ -165,7 +209,7 @@ private:
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
if (block_of_code.SpaceRemaining() < MINIMUM_REMAINING_CODESIZE) {
invalidate_entire_cache = true;
- PerformCacheInvalidation();
+ PerformRequestedCacheInvalidation(HaltReason::CacheInvalidation);
}
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
@@ -185,6 +229,42 @@ private:
Optimization::VerificationPass(ir_block);
return emitter.Emit(ir_block);
}
+
+ void PerformRequestedCacheInvalidation(HaltReason hr) {
+ if (Has(hr, HaltReason::CacheInvalidation)) {
+ std::unique_lock lock{invalidation_mutex};
+
+ ClearHalt(HaltReason::CacheInvalidation);
+
+ if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
+ return;
+ }
+
+ jit_state.ResetRSB();
+ if (invalidate_entire_cache) {
+ block_of_code.ClearCache();
+ emitter.ClearCache();
+ } else {
+ emitter.InvalidateCacheRanges(invalid_cache_ranges);
+ }
+ invalid_cache_ranges.clear();
+ invalidate_entire_cache = false;
+ }
+ }
+
+ A32JitState jit_state;
+ BlockOfCode block_of_code;
+ A32EmitX64 emitter;
+ Optimization::PolyfillOptions polyfill_options;
+
+ const A32::UserConfig conf;
+
+ Jit* jit_interface;
+
+ // Requests made during execution to invalidate the cache are queued up here.
+ bool invalidate_entire_cache = false;
+ boost::icl::interval_set<u32> invalid_cache_ranges;
+ std::mutex invalidation_mutex;
};
Jit::Jit(UserConfig conf)
@@ -193,46 +273,23 @@ Jit::Jit(UserConfig conf)
Jit::~Jit() = default;
HaltReason Jit::Run() {
- ASSERT(!is_executing);
- is_executing = true;
- SCOPE_EXIT {
- this->is_executing = false;
- };
-
- const HaltReason hr = impl->Execute();
-
- impl->PerformCacheInvalidation();
-
- return hr;
+ return impl->Run();
}
HaltReason Jit::Step() {
- ASSERT(!is_executing);
- is_executing = true;
- SCOPE_EXIT {
- this->is_executing = false;
- };
-
- const HaltReason hr = impl->Step();
-
- impl->PerformCacheInvalidation();
-
- return hr;
+ return impl->Step();
}
void Jit::ClearCache() {
- impl->invalidate_entire_cache = true;
- impl->RequestCacheInvalidation();
+ impl->ClearCache();
}
void Jit::InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
- impl->invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
- impl->RequestCacheInvalidation();
+ impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
- ASSERT(!is_executing);
- impl->jit_state = {};
+ impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
@@ -243,48 +300,44 @@ void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
-void Jit::ClearExclusiveState() {
- impl->ClearExclusiveState();
+std::array<std::uint32_t, 16>& Jit::Regs() {
+ return impl->Regs();
}
-std::array<u32, 16>& Jit::Regs() {
- return impl->jit_state.Reg;
+const std::array<std::uint32_t, 16>& Jit::Regs() const {
+ return impl->Regs();
}
-const std::array<u32, 16>& Jit::Regs() const {
- return impl->jit_state.Reg;
+
+std::array<std::uint32_t, 64>& Jit::ExtRegs() {
+ return impl->ExtRegs();
}
-std::array<u32, 64>& Jit::ExtRegs() {
- return impl->jit_state.ExtReg;
+const std::array<std::uint32_t, 64>& Jit::ExtRegs() const {
+ return impl->ExtRegs();
}
-const std::array<u32, 64>& Jit::ExtRegs() const {
- return impl->jit_state.ExtReg;
+std::uint32_t Jit::Cpsr() const {
+ return impl->Cpsr();
}
-u32 Jit::Cpsr() const {
- return impl->jit_state.Cpsr();
+void Jit::SetCpsr(std::uint32_t value) {
+ impl->SetCpsr(value);
}
-void Jit::SetCpsr(u32 value) {
- return impl->jit_state.SetCpsr(value);
+std::uint32_t Jit::Fpscr() const {
+ return impl->Fpscr();
}
-u32 Jit::Fpscr() const {
- return impl->jit_state.Fpscr();
+void Jit::SetFpscr(std::uint32_t value) {
+ impl->SetFpscr(value);
}
-void Jit::SetFpscr(u32 value) {
- return impl->jit_state.SetFpscr(value);
+void Jit::ClearExclusiveState() {
+ impl->ClearExclusiveState();
}
void Jit::DumpDisassembly() const {
- const size_t size = reinterpret_cast<const char*>(impl->block_of_code.getCurr()) - reinterpret_cast<const char*>(impl->block_of_code.GetCodeBegin());
- Common::DumpDisassembledX64(impl->block_of_code.GetCodeBegin(), size);
+ impl->DumpDisassembly();
}
-std::vector<std::string> Jit::Disassemble() const {
- const size_t size = reinterpret_cast<const char*>(impl->block_of_code.getCurr()) - reinterpret_cast<const char*>(impl->block_of_code.GetCodeBegin());
- return Common::DisassembleX64(impl->block_of_code.GetCodeBegin(), size);
-}
} // namespace Dynarmic::A32
diff --git a/src/dynarmic/backend/x64/a64_interface.cpp b/src/dynarmic/backend/x64/a64_interface.cpp
index 1ad0d196..57f75df0 100644
--- a/src/dynarmic/backend/x64/a64_interface.cpp
+++ b/src/dynarmic/backend/x64/a64_interface.cpp
@@ -69,7 +69,7 @@ public:
HaltReason Run() {
ASSERT(!is_executing);
- PerformRequestedCacheInvalidation();
+ PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
is_executing = true;
SCOPE_EXIT {
@@ -91,14 +91,14 @@ public:
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
- PerformRequestedCacheInvalidation();
+ PerformRequestedCacheInvalidation(hr);
return hr;
}
HaltReason Step() {
ASSERT(!is_executing);
- PerformRequestedCacheInvalidation();
+ PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
is_executing = true;
SCOPE_EXIT {
@@ -107,7 +107,7 @@ public:
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
- PerformRequestedCacheInvalidation();
+ PerformRequestedCacheInvalidation(hr);
return hr;
}
@@ -115,9 +115,7 @@ public:
void ClearCache() {
std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true;
- if (is_executing) {
- HaltExecution(HaltReason::CacheInvalidation);
- }
+ HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u64 start_address, size_t length) {
@@ -125,9 +123,7 @@ public:
const auto end_address = static_cast<u64>(start_address + length - 1);
const auto range = boost::icl::discrete_interval<u64>::closed(start_address, end_address);
invalid_cache_ranges.add(range);
- if (is_executing) {
- HaltExecution(HaltReason::CacheInvalidation);
- }
+ HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
@@ -268,7 +264,7 @@ private:
if (block_of_code.SpaceRemaining() < MINIMUM_REMAINING_CODESIZE) {
// Immediately evacuate cache
invalidate_entire_cache = true;
- PerformRequestedCacheInvalidation();
+ PerformRequestedCacheInvalidation(HaltReason::CacheInvalidation);
}
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
@@ -294,30 +290,26 @@ private:
return emitter.Emit(ir_block).entrypoint;
}
- void RequestCacheInvalidation() {
- if (is_executing) {
- HaltExecution(HaltReason::CacheInvalidation);
- return;
- }
+ void PerformRequestedCacheInvalidation(HaltReason hr) {
+ if (Has(hr, HaltReason::CacheInvalidation)) {
+ std::unique_lock lock{invalidation_mutex};
- PerformRequestedCacheInvalidation();
- }
+ ClearHalt(HaltReason::CacheInvalidation);
- void PerformRequestedCacheInvalidation() {
- std::unique_lock lock{invalidation_mutex};
- if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
- return;
- }
+ if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
+ return;
+ }
- jit_state.ResetRSB();
- if (invalidate_entire_cache) {
- block_of_code.ClearCache();
- emitter.ClearCache();
- } else {
- emitter.InvalidateCacheRanges(invalid_cache_ranges);
+ jit_state.ResetRSB();
+ if (invalidate_entire_cache) {
+ block_of_code.ClearCache();
+ emitter.ClearCache();
+ } else {
+ emitter.InvalidateCacheRanges(invalid_cache_ranges);
+ }
+ invalid_cache_ranges.clear();
+ invalidate_entire_cache = false;
}
- invalid_cache_ranges.clear();
- invalidate_entire_cache = false;
}
bool is_executing = false;