mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 03:30:15 +00:00
cache fixup
This commit is contained in:
parent
6803ceb886
commit
c428dcddce
130
src/core/arm/cache/cache.cpp
vendored
130
src/core/arm/cache/cache.cpp
vendored
@ -7,21 +7,53 @@
|
||||
|
||||
namespace Cache {
|
||||
|
||||
static std::list<CacheBase*> caches;
|
||||
|
||||
void RegisterCode(u32 address, u32 size) {
|
||||
for (auto const& cache : caches) {
|
||||
cache->OnCodeLoad(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterCode(u32 address, u32 size) {
|
||||
for (auto const& cache : caches) {
|
||||
cache->OnCodeUnload(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
for (auto const& cache : caches) {
|
||||
cache->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void RegisterCache(CacheBase* cache) {
|
||||
caches.push_back(cache);
|
||||
}
|
||||
|
||||
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);
|
||||
g_cachemanager.RegisterCache(this);
|
||||
RegisterCache(this);
|
||||
}
|
||||
|
||||
CacheBase::~CacheBase() {
|
||||
g_cachemanager.UnregisterCache(this);
|
||||
UnregisterCache(this);
|
||||
}
|
||||
|
||||
void CacheBase::Clear() {
|
||||
if (OnClearCallback != nullptr) OnClearCallback();
|
||||
if (OnClearCallback != nullptr) {
|
||||
OnClearCallback();
|
||||
}
|
||||
|
||||
for (auto& cache : ptr_caches) cache.data.assign(cache.data.size(), nullptr);
|
||||
for (auto& cache : ptr_caches) {
|
||||
cache.data.assign(cache.data.size(), nullptr);
|
||||
}
|
||||
|
||||
if (index_mode) {
|
||||
blocks_pc.assign(MAX_BLOCKS, INVALID_BLOCK);
|
||||
@ -30,18 +62,25 @@ void CacheBase::Clear() {
|
||||
}
|
||||
|
||||
bool CacheBase::RemoveBlock(u32 pc) {
|
||||
u8** ptr = page_pointers[pc >> Memory::PAGE_BITS];
|
||||
void** ptr = page_pointers[pc >> Memory::PAGE_BITS];
|
||||
if (ptr != nullptr) {
|
||||
ptr = &ptr[pc & Memory::PAGE_MASK];
|
||||
if (*ptr == nullptr) return false;
|
||||
|
||||
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;
|
||||
if (id < next_block) {
|
||||
next_block = id;
|
||||
}
|
||||
while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) {
|
||||
--num_blocks;
|
||||
}
|
||||
}
|
||||
*ptr = nullptr;
|
||||
return true;
|
||||
@ -52,17 +91,24 @@ bool CacheBase::RemoveBlock(u32 pc) {
|
||||
bool CacheBase::RemoveRange(u32 start, u32 end) {
|
||||
bool result = false;
|
||||
for (auto& cache : ptr_caches) {
|
||||
for (int i = std::max(start, cache.addr); i < std::min(end, cache.addr_end); ++i) {
|
||||
u8** ptr = &cache.data[i - cache.addr];
|
||||
if (*ptr == nullptr) continue;
|
||||
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;
|
||||
if (id < next_block) {
|
||||
next_block = id;
|
||||
}
|
||||
while (num_blocks > 0 && blocks_pc[num_blocks - 1] == INVALID_BLOCK) {
|
||||
--num_blocks;
|
||||
}
|
||||
}
|
||||
*ptr = nullptr;
|
||||
result = true;
|
||||
@ -75,23 +121,29 @@ 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));
|
||||
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]; }
|
||||
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(),
|
||||
[&](auto const& cache) {
|
||||
[&, this](auto const& cache) {
|
||||
if ((address < cache.addr_end) && (address + size > cache.addr)) {
|
||||
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; }
|
||||
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;
|
||||
@ -99,55 +151,39 @@ void CacheBase::OnCodeUnload(u32 address, u32 size) {
|
||||
ptr_caches.cend());
|
||||
}
|
||||
|
||||
u8*& CacheBase::GetNewPtr(u32 pc) {
|
||||
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);
|
||||
|
||||
u8** page_ptr = page_pointers[pc >> Memory::PAGE_BITS];
|
||||
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];
|
||||
}
|
||||
|
||||
u8** block_ptr = &page_ptr[pc & Memory::PAGE_MASK];
|
||||
void** block_ptr = &page_ptr[pc & Memory::PAGE_MASK];
|
||||
|
||||
DEBUG_ASSERT(*block_ptr == nullptr);
|
||||
|
||||
if (index_mode) {
|
||||
if (next_block == MAX_BLOCKS) Clear();
|
||||
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++;
|
||||
do {
|
||||
++next_block;
|
||||
} while (next_block <= num_blocks && blocks_pc[next_block] != INVALID_BLOCK);
|
||||
|
||||
if (next_block > num_blocks) {
|
||||
num_blocks++;
|
||||
}
|
||||
}
|
||||
|
||||
return *block_ptr;
|
||||
}
|
||||
|
||||
|
||||
void CacheManager::RegisterCode(u32 address, u32 size) const {
|
||||
for (auto const& cache : caches) cache->OnCodeLoad(address, size);
|
||||
}
|
||||
|
||||
void CacheManager::UnregisterCode(u32 address, u32 size) const {
|
||||
for (auto const& cache : caches) cache->OnCodeUnload(address, size);
|
||||
}
|
||||
|
||||
void CacheManager::ClearCache() const {
|
||||
for (auto const& cache : caches) cache->Clear();
|
||||
}
|
||||
|
||||
void CacheManager::RegisterCache(CacheBase* cache) {
|
||||
caches.push_back(cache);
|
||||
}
|
||||
|
||||
void CacheManager::UnregisterCache(CacheBase* cache) {
|
||||
caches.erase(std::remove(caches.begin(), caches.end(), cache), caches.end());
|
||||
}
|
||||
|
||||
CacheManager g_cachemanager;
|
||||
|
||||
}
|
||||
|
75
src/core/arm/cache/cache.h
vendored
75
src/core/arm/cache/cache.h
vendored
@ -17,6 +17,14 @@
|
||||
|
||||
namespace Cache {
|
||||
|
||||
/// Loaders call these when mapping/unmapping code
|
||||
void RegisterCode(u32 address, u32 size);
|
||||
void UnregisterCode(u32 address, u32 size = 1);
|
||||
|
||||
/// Clear every cache
|
||||
void ClearCache();
|
||||
|
||||
|
||||
using OnClearCb = std::function<void()>;
|
||||
|
||||
const u32 MAX_BLOCKS = 0x40000;
|
||||
@ -25,7 +33,7 @@ const u32 INVALID_BLOCK = 0xFFFFFFFF;
|
||||
struct BlockPtrCache {
|
||||
u32 addr;
|
||||
u32 addr_end;
|
||||
std::vector<u8*> data;
|
||||
std::vector<void*> data;
|
||||
};
|
||||
|
||||
class CacheBase {
|
||||
@ -35,7 +43,9 @@ protected:
|
||||
|
||||
public:
|
||||
/// Called when the cache needs to reset or Clear() is called
|
||||
void SetClearCallback(OnClearCb cb) { OnClearCallback = cb; }
|
||||
void SetClearCallback(OnClearCb cb) {
|
||||
OnClearCallback = cb;
|
||||
}
|
||||
|
||||
/// Clear and call clear callback
|
||||
void Clear();
|
||||
@ -47,25 +57,25 @@ public:
|
||||
void OnCodeUnload(u32 address, u32 size);
|
||||
|
||||
protected:
|
||||
u8* GetPtr(u32 pc) const {
|
||||
u8** ptr = page_pointers[pc >> Memory::PAGE_BITS];
|
||||
void* GetPtr(u32 pc) const {
|
||||
void** 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];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
u8*& GetNewPtr(u32 pc);
|
||||
void*& GetNewPtr(u32 pc);
|
||||
|
||||
std::function<u8*(u32)> id_to_pointer;
|
||||
std::function<u32(u8*)> pointer_to_id;
|
||||
std::function<void*(u32)> id_to_pointer;
|
||||
std::function<u32(void*)> pointer_to_id;
|
||||
|
||||
private:
|
||||
bool index_mode;
|
||||
OnClearCb OnClearCallback = nullptr;
|
||||
|
||||
std::vector<BlockPtrCache> ptr_caches;
|
||||
std::array<u8**, (1 << (32 - Memory::PAGE_BITS))> page_pointers;
|
||||
std::array<void**, (1 << (32 - Memory::PAGE_BITS))> page_pointers;
|
||||
|
||||
std::vector<u32> blocks_pc;
|
||||
u32 next_block = 0;
|
||||
@ -82,10 +92,14 @@ public:
|
||||
~PtrCache() {}
|
||||
|
||||
/// Get cached pointer for PC
|
||||
T FindPtr(u32 pc) { return reinterpret_cast<T>(GetPtr(pc)); }
|
||||
T GetPtr(u32 pc) {
|
||||
return reinterpret_cast<T>(CacheBase::GetPtr(pc));
|
||||
}
|
||||
|
||||
/// Get reference of pointer for PC
|
||||
T& GetNewPtr(u32 pc) { return reinterpret_cast<T&>(CacheBase::GetNewPtr(pc)); }
|
||||
T& GetNewPtr(u32 pc) {
|
||||
return reinterpret_cast<T&>(CacheBase::GetNewPtr(pc));
|
||||
}
|
||||
};
|
||||
|
||||
/// Index based cache
|
||||
@ -93,45 +107,30 @@ template <typename T>
|
||||
class Cache final : public CacheBase {
|
||||
public:
|
||||
explicit Cache(OnClearCb clearcb = nullptr) : CacheBase(true, clearcb) {
|
||||
id_to_pointer = [this](u32 id) -> u8* {
|
||||
return reinterpret_cast<u8*>(&blocks[id]);
|
||||
id_to_pointer = [this](u32 id) -> void* {
|
||||
return &blocks[id];
|
||||
};
|
||||
pointer_to_id = [this](u8* ptr) -> u32 {
|
||||
return static_cast<u32>(reinterpret_cast<T*>(ptr) - &blocks[0]);
|
||||
pointer_to_id = [this](void* ptr) -> u32 {
|
||||
return static_cast<u32>(std::distance(blocks.begin(),
|
||||
std::find_if(blocks.begin(), blocks.end(), [&](auto const& block) {
|
||||
return (reinterpret_cast<T*>(ptr) == &block) ? true : false;
|
||||
})));
|
||||
};
|
||||
}
|
||||
~Cache() {}
|
||||
|
||||
/// Get block cached for PC
|
||||
T* FindBlock(u32 pc) { return reinterpret_cast<T*>(GetPtr(pc)); }
|
||||
T* GetBlock(u32 pc) {
|
||||
return reinterpret_cast<T*>(GetPtr(pc));
|
||||
}
|
||||
|
||||
/// Allocate block for PC
|
||||
T& GetNewBlock(u32 pc) { return *reinterpret_cast<T*&>(GetNewPtr(pc)); }
|
||||
T& GetNewBlock(u32 pc) {
|
||||
return *reinterpret_cast<T*&>(GetNewPtr(pc));
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<T, MAX_BLOCKS> blocks;
|
||||
};
|
||||
|
||||
class CacheManager {
|
||||
public:
|
||||
CacheManager() {}
|
||||
~CacheManager() {}
|
||||
|
||||
/// Loaders call these when mapping/unmapping code
|
||||
void RegisterCode(u32 address, u32 size) const;
|
||||
void UnregisterCode(u32 address, u32 size = 1) const;
|
||||
|
||||
/// Clear every cache
|
||||
void ClearCache() const;
|
||||
|
||||
private:
|
||||
std::list<CacheBase*> caches;
|
||||
|
||||
public:
|
||||
void RegisterCache(CacheBase* cache);
|
||||
void UnregisterCache(CacheBase* cache);
|
||||
};
|
||||
|
||||
extern CacheManager g_cachemanager;
|
||||
|
||||
}
|
||||
|
@ -3893,10 +3893,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
cpu->Reg[15] &= 0xfffffffc;
|
||||
|
||||
//clear cache if we dont have more than 10kb of buffer remaining
|
||||
if ((top + (10 * 1024)) >= CACHE_BUFFER_SIZE) instr_cache.Clear();
|
||||
if ((top + (10 * 1024)) >= CACHE_BUFFER_SIZE) {
|
||||
instr_cache.Clear();
|
||||
}
|
||||
|
||||
// Find the cached instruction cream, otherwise translate it...
|
||||
ptr = instr_cache.FindPtr(cpu->Reg[15]);
|
||||
ptr = instr_cache.GetPtr(cpu->Reg[15]);
|
||||
if (ptr == nullptr) {
|
||||
ptr = instr_cache.GetNewPtr(cpu->Reg[15]) = reinterpret_cast<u8*>(&inst_buf[top]);
|
||||
if (cpu->NumInstrsToExecute != 1) {
|
||||
|
@ -122,8 +122,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
||||
MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private);
|
||||
|
||||
// Map cache
|
||||
Cache::g_cachemanager.UnregisterCode(0, 0xFFFFFFFF);
|
||||
Cache::g_cachemanager.RegisterCode(codeset->code.addr, codeset->code.size);
|
||||
Cache::UnregisterCode(0, 0xFFFFFFFF);
|
||||
Cache::RegisterCode(codeset->code.addr, codeset->code.size);
|
||||
|
||||
// Allocate and map stack
|
||||
vm_manager.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
|
||||
|
Loading…
Reference in New Issue
Block a user