diff --git a/src/core/arm/cache/cache.cpp b/src/core/arm/cache/cache.cpp index 678b16784..0d9610a0c 100644 --- a/src/core/arm/cache/cache.cpp +++ b/src/core/arm/cache/cache.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" #include "core/arm/cache/cache.h" namespace Cache { @@ -10,20 +9,20 @@ namespace Cache { static std::list caches; void RegisterCode(u32 address, u32 size) { - for (auto const& cache : caches) { - cache->OnCodeLoad(address, size); + for (const auto& cache : caches) { + cache->cache_fns.code_load(address, size); } } void UnregisterCode(u32 address, u32 size) { - for (auto const& cache : caches) { - cache->OnCodeUnload(address, size); + for (const auto& cache : caches) { + cache->cache_fns.code_unload(address, size); } } void ClearCache() { - for (auto const& cache : caches) { - cache->Clear(); + for (const auto& cache : caches) { + cache->cache_fns.clear(); } } @@ -35,10 +34,7 @@ static void UnregisterCache(CacheBase* cache) { caches.erase(std::remove(caches.begin(), caches.end(), cache), caches.end()); } -CacheBase::CacheBase(bool index_mode, OnClearCb clearcb) : index_mode(index_mode) { - page_pointers.fill(nullptr); - Clear(); - SetClearCallback(clearcb); +CacheBase::CacheBase(functions fns) : cache_fns(fns) { RegisterCache(this); } @@ -46,144 +42,4 @@ CacheBase::~CacheBase() { UnregisterCache(this); } -void CacheBase::Clear() { - if (OnClearCallback != nullptr) { - OnClearCallback(); - } - - for (auto& cache : ptr_caches) { - cache.data.assign(cache.data.size(), nullptr); - } - - if (index_mode) { - blocks_pc.assign(MAX_BLOCKS, INVALID_BLOCK); - next_block = num_blocks = 0; - } -} - -bool CacheBase::RemoveBlock(u32 pc) { - void** ptr = page_pointers[pc >> Memory::PAGE_BITS]; - if (ptr != nullptr) { - ptr = &ptr[pc & Memory::PAGE_MASK]; - - if (*ptr == nullptr) { - return false; - } - - if (index_mode) { - const u32 id = pointer_to_id(*ptr); - ASSERT(blocks_pc[id] == pc); - - blocks_pc[id] = INVALID_BLOCK; - if (id < next_block) { - next_block = id; - } - while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) { - --num_blocks; - } - } - *ptr = nullptr; - return true; - } - return false; -} - -bool CacheBase::RemoveRange(u32 start, u32 end) { - bool result = false; - for (auto& cache : ptr_caches) { - for (u32 i = std::max(start, cache.addr); i < std::min(end, cache.addr_end); ++i) { - void** ptr = &cache.data[i - cache.addr]; - - if (*ptr == nullptr) { - continue; - } - - if (index_mode) { - const u32 id = pointer_to_id(*ptr); - ASSERT(blocks_pc[id] == i); - - blocks_pc[id] = INVALID_BLOCK; - if (id < next_block) { - next_block = id; - } - while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) { - --num_blocks; - } - } - *ptr = nullptr; - result = true; - } - } - return result; -} - -void CacheBase::OnCodeLoad(u32 address, u32 size) { - const u32 end = address + size; - - // Check there is no overlapping - for (auto const& cache : ptr_caches) { - ASSERT((address >= cache.addr_end) || (end <= cache.addr)); - } - - ASSERT((address & Memory::PAGE_MASK) == 0 && (size & Memory::PAGE_MASK) == 0); - - BlockPtrCache cache{ address, address + size }; - cache.data.assign(size, nullptr); - - for (u32 i = address; i < end; i += Memory::PAGE_SIZE) { - page_pointers[i >> Memory::PAGE_BITS] = &cache.data[i - address]; - } - ptr_caches.emplace_back(std::move(cache)); -} - -void CacheBase::OnCodeUnload(u32 address, u32 size) { - ptr_caches.erase(std::remove_if(ptr_caches.begin(), ptr_caches.end(), - [&, this](auto const& cache) { - if ((address < cache.addr_end) && (address + size > cache.addr)) { - this->RemoveRange(cache.addr, cache.addr_end); - for (u32 i = cache.addr; i < cache.addr_end; i += Memory::PAGE_SIZE) { - page_pointers[i >> Memory::PAGE_BITS] = nullptr; - } - return true; - } - return false; - }), - ptr_caches.cend()); -} - -void*& CacheBase::GetNewPtr(u32 pc) { - DEBUG_ASSERT(!index_mode || next_block == MAX_BLOCKS || ((next_block < MAX_BLOCKS) && blocks_pc[next_block] == INVALID_BLOCK)); - DEBUG_ASSERT(GetPtr(pc) == nullptr); - - void** page_ptr = page_pointers[pc >> Memory::PAGE_BITS]; - if (page_ptr == nullptr) { - // pc isnt within mapped code - OnCodeLoad(pc & ~Memory::PAGE_MASK, Memory::PAGE_SIZE); - page_ptr = page_pointers[pc >> Memory::PAGE_BITS]; - } - - void** block_ptr = &page_ptr[pc & Memory::PAGE_MASK]; - - DEBUG_ASSERT(*block_ptr == nullptr); - - if (index_mode) { - if (next_block == MAX_BLOCKS) { - Clear(); - } - - blocks_pc[next_block] = pc; - *block_ptr = id_to_pointer(next_block); - - do { - ++next_block; - } while (next_block <= num_blocks && blocks_pc[next_block] != INVALID_BLOCK); - - if (next_block > num_blocks) { - num_blocks++; - } - } - - return *block_ptr; -} - } diff --git a/src/core/arm/cache/cache.h b/src/core/arm/cache/cache.h index e6578a6bc..971f1dcbd 100644 --- a/src/core/arm/cache/cache.h +++ b/src/core/arm/cache/cache.h @@ -27,19 +27,41 @@ void ClearCache(); using OnClearCb = std::function; -const u32 MAX_BLOCKS = 0x40000; -const u32 INVALID_BLOCK = 0xFFFFFFFF; +constexpr u32 MAX_BLOCKS = 0x40000; +constexpr u32 INVALID_BLOCK = 0xFFFFFFFF; +template struct BlockPtrCache { u32 addr; u32 addr_end; - std::vector data; + std::vector data; }; class CacheBase { +public: + struct functions { + std::function code_load; + std::function code_unload; + std::function clear; + } const cache_fns; + protected: - explicit CacheBase(bool index_mode, OnClearCb clearcb); + explicit CacheBase(functions fns); ~CacheBase(); +}; + +template +class CacheCommon : CacheBase { +protected: + explicit CacheCommon(bool index_mode, OnClearCb clearcb) : index_mode(index_mode), CacheBase({ + [this](u32 start, u32 size) { this->OnCodeLoad(start, size); }, + [this](u32 start, u32 size) { this->OnCodeUnload(start, size); }, + [this]() { this->Clear(); }}) { + static_assert(std::is_pointer::value, "T must be a pointer"); + Clear(); + SetClearCallback(clearcb); + } + ~CacheCommon() {} public: /// Called when the cache needs to reset or Clear() is called @@ -48,34 +70,165 @@ public: } /// Clear and call clear callback - void Clear(); - // returns true if block was found, false otherwise - bool RemoveBlock(u32 pc); - bool RemoveRange(u32 start, u32 end); + void Clear() { + if (OnClearCallback != nullptr) { + OnClearCallback(); + } - void OnCodeLoad(u32 address, u32 size); - void OnCodeUnload(u32 address, u32 size); + for (auto& cache : ptr_caches) { + cache.data.assign(cache.data.size(), nullptr); + } + + if (index_mode) { + blocks_pc.assign(MAX_BLOCKS, INVALID_BLOCK); + next_block = num_blocks = 0; + } + } + + /// Returns true if block was found, false otherwise + bool RemoveBlock(u32 pc) { + T* ptr = page_pointers[pc >> Memory::PAGE_BITS]; + if (ptr != nullptr) { + ptr = &ptr[pc & Memory::PAGE_MASK]; + + if (*ptr == nullptr) { + return false; + } + + if (index_mode) { + const u32 id = pointer_to_index(*ptr); + ASSERT(blocks_pc[id] == pc); + + blocks_pc[id] = INVALID_BLOCK; + next_block = std::min(id, next_block); + + while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) { + --num_blocks; + } + } + *ptr = nullptr; + return true; + } + return false; + } + + /// Returns true if at least one block was found, false otherwise + bool RemoveRange(u32 start, u32 end) { + bool result = false; + for (auto& cache : ptr_caches) { + for (u32 i = std::max(start, cache.addr); i < std::min(end, cache.addr_end); ++i) { + T* ptr = &cache.data[i - cache.addr]; + + if (*ptr == nullptr) { + continue; + } + + if (index_mode) { + const u32 id = pointer_to_index(*ptr); + ASSERT(blocks_pc[id] == i); + + blocks_pc[id] = INVALID_BLOCK; + next_block = std::min(id, next_block); + + while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) { + --num_blocks; + } + } + *ptr = nullptr; + result = true; + } + } + return result; + } + + void OnCodeLoad(u32 address, u32 size) { + // Check there is no overlapping + ASSERT(std::none_of(ptr_caches.cbegin(), ptr_caches.cend(), + [&](const auto& cache) { + return (address < cache.addr_end) && (address + size > cache.addr); + })); + + ASSERT((address & Memory::PAGE_MASK) == 0 && (size & Memory::PAGE_MASK) == 0); + + BlockPtrCache cache{ address, address + size }; + cache.data.assign(size, nullptr); + + for (u32 i = address; i < address + size; i += Memory::PAGE_SIZE) { + page_pointers[i >> Memory::PAGE_BITS] = &cache.data[i - address]; + } + ptr_caches.emplace_back(std::move(cache)); + } + + void OnCodeUnload(u32 address, u32 size) { + ptr_caches.erase(std::remove_if(ptr_caches.begin(), ptr_caches.end(), + [&, this](const auto& cache) { + if ((address < cache.addr_end) && (address + size > cache.addr)) { + this->RemoveRange(cache.addr, cache.addr_end); + for (u32 i = cache.addr; i < cache.addr_end; i += Memory::PAGE_SIZE) { + page_pointers[i >> Memory::PAGE_BITS] = nullptr; + } + return true; + } + return false; + }), + ptr_caches.cend()); + } protected: - void* GetPtr(u32 pc) const { - void** ptr = page_pointers[pc >> Memory::PAGE_BITS]; + T GetPtr(u32 pc) const { + T* ptr = page_pointers[pc >> Memory::PAGE_BITS]; if (ptr != nullptr) { - DEBUG_ASSERT(!index_mode || blocks_pc[pointer_to_id(ptr[pc & Memory::PAGE_MASK])] == pc); - return ptr[pc & Memory::PAGE_MASK]; + const T value = ptr[pc & Memory::PAGE_MASK]; + DEBUG_ASSERT(!index_mode || value == nullptr || blocks_pc[pointer_to_index(ptr[pc & Memory::PAGE_MASK])] == pc); + return value; } return nullptr; } - void*& GetNewPtr(u32 pc); - std::function id_to_pointer; - std::function pointer_to_id; + T* GetNewPtr(u32 pc) { + DEBUG_ASSERT(!index_mode || next_block == MAX_BLOCKS || ((next_block < MAX_BLOCKS) && blocks_pc[next_block] == INVALID_BLOCK)); + DEBUG_ASSERT(GetPtr(pc) == nullptr); + + T* page_ptr = page_pointers[pc >> Memory::PAGE_BITS]; + if (page_ptr == nullptr) { + // pc isnt within mapped code + OnCodeLoad(pc & ~Memory::PAGE_MASK, Memory::PAGE_SIZE); + page_ptr = page_pointers[pc >> Memory::PAGE_BITS]; + } + + T* block_ptr = &page_ptr[pc & Memory::PAGE_MASK]; + + DEBUG_ASSERT(!index_mode || *block_ptr == nullptr); + + if (index_mode) { + if (next_block == MAX_BLOCKS) { + Clear(); + } + + blocks_pc[next_block] = pc; + *block_ptr = index_to_pointer(next_block); + + do { + ++next_block; + } while (next_block <= num_blocks && blocks_pc[next_block] != INVALID_BLOCK); + + if (next_block > num_blocks) { + num_blocks++; + } + } + + return block_ptr; + } + + std::function index_to_pointer; + std::function pointer_to_index; private: - bool index_mode; OnClearCb OnClearCallback = nullptr; + const bool index_mode; - std::vector ptr_caches; - std::array page_pointers; + std::vector> ptr_caches; + std::array page_pointers = {}; std::vector blocks_pc; u32 next_block = 0; @@ -84,49 +237,52 @@ private: /// Use this if you only need to store a pointer template -class PtrCache final : public CacheBase { +class PtrCache final : public CacheCommon { public: - explicit PtrCache(OnClearCb clearcb = nullptr) : CacheBase(false, clearcb) { - static_assert(std::is_pointer::value, "T must be a pointer"); + explicit PtrCache(OnClearCb clearcb = nullptr) : CacheCommon(false, clearcb) { + static_assert(std::is_pointer::value, "T must be a pointer, use IndexedCache"); } ~PtrCache() {} /// Get cached pointer for PC T GetPtr(u32 pc) { - return reinterpret_cast(CacheBase::GetPtr(pc)); + return CacheCommon::GetPtr(pc); } - /// Get reference of pointer for PC - T& GetNewPtr(u32 pc) { - return reinterpret_cast(CacheBase::GetNewPtr(pc)); + /// Set pointer for PC to value + T SetPtr(u32 pc, T value) { + return (*CacheCommon::GetNewPtr(pc) = value); } }; /// Index based cache template -class Cache final : public CacheBase { +class IndexedCache final : public CacheCommon { public: - explicit Cache(OnClearCb clearcb = nullptr) : CacheBase(true, clearcb) { - id_to_pointer = [this](u32 id) -> void* { + explicit IndexedCache(OnClearCb clearcb = nullptr) : CacheCommon(true, clearcb) { + + static_assert(!std::is_pointer::value, "T can't be a pointer, use PtrCache"); + + CacheCommon::index_to_pointer = [this](u32 id) -> T* { return &blocks[id]; }; - pointer_to_id = [this](void* ptr) -> u32 { - return static_cast(std::distance(blocks.begin(), - std::find_if(blocks.begin(), blocks.end(), [&](auto const& block) { - return (reinterpret_cast(ptr) == &block) ? true : false; + CacheCommon::pointer_to_index = [this](T* ptr) -> u32 { + return static_cast(std::distance(blocks.cbegin(), + std::find_if(blocks.cbegin(), blocks.cend(), [&](const T& block) { + return (ptr == &block); }))); }; } - ~Cache() {} + ~IndexedCache() {} /// Get block cached for PC T* GetBlock(u32 pc) { - return reinterpret_cast(GetPtr(pc)); + return CacheCommon::GetPtr(pc); } /// Allocate block for PC - T& GetNewBlock(u32 pc) { - return *reinterpret_cast(GetNewPtr(pc)); + T* GetNewBlock(u32 pc) { + return *CacheCommon::GetNewPtr(pc); } private: diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 1aea759de..964f53e8e 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -1133,7 +1133,7 @@ struct pkh_inst { typedef arm_inst * ARM_INST_PTR; #define CACHE_BUFFER_SIZE (64 * 1024 * 2000) -static char inst_buf[CACHE_BUFFER_SIZE]; +static u8 inst_buf[CACHE_BUFFER_SIZE]; static int top = 0; static inline void *AllocBuffer(unsigned int size) { int start = top; @@ -3900,7 +3900,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { // Find the cached instruction cream, otherwise translate it... ptr = instr_cache.GetPtr(cpu->Reg[15]); if (ptr == nullptr) { - ptr = instr_cache.GetNewPtr(cpu->Reg[15]) = reinterpret_cast(&inst_buf[top]); + ptr = instr_cache.SetPtr(cpu->Reg[15], &inst_buf[top]); if (cpu->NumInstrsToExecute != 1) { if (InterpreterTranslateBlock(cpu, cpu->Reg[15]) == FETCH_EXCEPTION) goto END;