mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 09:19:42 -10:00
sh2: precise recompiler block invalidation
- Update dirty bit field on invalidate so overlapping blocks can be lazily evicted - Track "generation" to allow entire cached memory area to be lazily evicted - Invalidate appropriate cache line/memory area on cache purge
This commit is contained in:
1 parent
462afe89f8
commit
241f0e9a36
4 files changed
+60
-8
No files matched your search
@@ -20,17 +20,49 @@
|
||||
#define ET Reg(ET)
|
||||
#define ID Reg(ID)
|
||||
|
||||
auto SH2::Recompiler::invalidate(u32 address) -> void {
|
||||
auto SH2::Recompiler::mask(u8 address, u8 size) -> u64 {
|
||||
//1 bit per 4 bytes
|
||||
u6 s = address >> 2;
|
||||
u6 e = address + size - 1 >> 2;
|
||||
u64 smask = ~0ull << s;
|
||||
u64 emask = ~0ull >> 63 - e;
|
||||
assert(s <= e);
|
||||
return smask & emask;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::invalidate(u32 address, u8 size) -> void {
|
||||
auto pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) return;
|
||||
memory::jitprotect(false);
|
||||
pool->blocks[address >> 1 & 0x7f] = nullptr;
|
||||
pool->dirty |= mask(address, size);
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto& pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
if(!pool) {
|
||||
pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
memory::jitprotect(false);
|
||||
pool->generation = generation;
|
||||
memory::jitprotect(true);
|
||||
} else if(address >> 29 == Area::Cached && pool->generation != generation) {
|
||||
memory::jitprotect(false);
|
||||
for(auto& block : pool->blocks) block = nullptr;
|
||||
pool->generation = generation;
|
||||
pool->dirty = 0;
|
||||
memory::jitprotect(true);
|
||||
} else if(pool->dirty) {
|
||||
memory::jitprotect(false);
|
||||
u8 address = 0;
|
||||
for(auto& block : pool->blocks) {
|
||||
if(block && (pool->dirty & mask(address, block->size)) != 0) {
|
||||
block = nullptr;
|
||||
}
|
||||
address += 2;
|
||||
}
|
||||
pool->dirty = 0;
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
@@ -54,6 +86,7 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
beginFunction(2);
|
||||
|
||||
u32 start = address;
|
||||
bool hasBranched = 0;
|
||||
inDelaySlot = 1; //force runtime check on first instruction
|
||||
while(true) {
|
||||
@@ -72,6 +105,7 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
|
||||
memory::jitprotect(false);
|
||||
block->code = endFunction();
|
||||
block->size = address - start;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@@ -258,17 +258,25 @@ struct SH2 {
|
||||
}
|
||||
|
||||
u8* code;
|
||||
u8 size;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
u32 generation;
|
||||
u64 dirty;
|
||||
Block* blocks[1 << 7];
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
generation = 0;
|
||||
for(u32 index : range(1 << 24)) pools[index] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void;
|
||||
auto invalidateCached() -> void {
|
||||
generation++;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address, u8 size) -> void;
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 address) -> Block*;
|
||||
auto emit(u32 address) -> Block*;
|
||||
@@ -277,7 +285,10 @@ struct SH2 {
|
||||
auto setSR(reg src) -> void;
|
||||
template<typename F> auto checkDelaySlot(F body) -> void;
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 24];
|
||||
} recompiler{*this};
|
||||
|
||||
@@ -124,7 +124,7 @@ template<u32 Origin> auto SH2::readLong(u32 address) -> u32 {
|
||||
|
||||
template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address);
|
||||
recompiler.invalidate(address, 1);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
@@ -173,7 +173,7 @@ template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address);
|
||||
recompiler.invalidate(address, 2);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
@@ -219,8 +219,7 @@ template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address + 0);
|
||||
recompiler.invalidate(address + 2);
|
||||
recompiler.invalidate(address, 4);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
|
||||
@@ -88,12 +88,20 @@ auto SH2::Cache::purge(u32 address) -> void {
|
||||
if(tags[Way1 | entry] == tag) tags[Way1 | entry] |= Invalid;
|
||||
if(tags[Way2 | entry] == tag) tags[Way2 | entry] |= Invalid;
|
||||
if(tags[Way3 | entry] == tag) tags[Way3 | entry] |= Invalid;
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
self->recompiler.invalidate(address & 0x1fff'fff0, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
template<u32 Ways>
|
||||
auto SH2::Cache::purge() -> void {
|
||||
for(auto index : range(64)) lrus[index] = 0;
|
||||
for(auto index : range(64 * Ways)) tags[index] |= Invalid;
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
self->recompiler.invalidateCached();
|
||||
}
|
||||
}
|
||||
|
||||
auto SH2::Cache::power() -> void {
|
||||
|
||||
Reference in new issue
Block a user