diff --git a/src/core/arm/nce/lru_cache.h b/src/core/arm/nce/lru_cache.h new file mode 100644 index 0000000000..47125922cb --- /dev/null +++ b/src/core/arm/nce/lru_cache.h @@ -0,0 +1,57 @@ +#pragma once + +#include <list> +#include <unordered_map> + +template<typename KeyType, typename ValueType> +class LRUCache { +public: + explicit LRUCache(size_t capacity) : capacity(capacity) {} + + ValueType* get(const KeyType& key) { + auto it = cache_map.find(key); + if (it == cache_map.end()) { + return nullptr; + } + + // Move the accessed item to the front of the list (most recently used) + cache_list.splice(cache_list.begin(), cache_list, it->second.first); + return &(it->second.second); + } + + void put(const KeyType& key, const ValueType& value) { + auto it = cache_map.find(key); + + if (it != cache_map.end()) { + // Key exists, update value and move to front + it->second.second = value; + cache_list.splice(cache_list.begin(), cache_list, it->second.first); + return; + } + + // Remove the least recently used item if cache is full + if (cache_map.size() >= capacity) { + auto last = cache_list.back(); + cache_map.erase(last); + cache_list.pop_back(); + } + + // Insert new item at the front + cache_list.push_front(key); + cache_map[key] = {cache_list.begin(), value}; + } + + void clear() { + cache_map.clear(); + cache_list.clear(); + } + + size_t size() const { + return cache_map.size(); + } + +private: + size_t capacity; + std::list<KeyType> cache_list; + std::unordered_map<KeyType, std::pair<typename std::list<KeyType>::iterator, ValueType>> cache_map; +}; \ No newline at end of file diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index a44f385e2e..21ea7fd2a1 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h @@ -13,6 +13,7 @@ #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_typed_address.h" #include "core/hle/kernel/physical_memory.h" +#include "lru_cache.h" namespace Core::NCE { @@ -60,8 +61,20 @@ private: void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg); private: + static constexpr size_t CACHE_SIZE = 1024; // Cache size for patch entries + LRUCache<uintptr_t, PatchTextAddress> patch_cache{CACHE_SIZE}; + void BranchToPatch(uintptr_t module_dest) { - curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); + // Try to get existing patch entry from cache + if (auto* cached_patch = patch_cache.get(module_dest)) { + curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), *cached_patch}); + return; + } + + // If not in cache, create new entry and cache it + const auto patch_addr = c.offset(); + curr_patch->m_branch_to_patch_relocations.push_back({patch_addr, module_dest}); + patch_cache.put(module_dest, patch_addr); } void BranchToModule(uintptr_t module_dest) {