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:
invertego authored and Luke Usher committed 2023-03-29 06:37:34 +00:00
1 parent 462afe89f8
commit 241f0e9a36
4 files changed
+60 -8

No files matched your search

+37 -3
View File
@@ -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;
}
+12 -1
View File
@@ -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};
+3 -4
View File
@@ -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 {