Merge pull request #4647 from degasus/jitcache

JitCache: Clean up block id handling.
This commit is contained in:
Matthew Parlane 2017-01-13 09:00:46 +13:00 committed by GitHub
commit 356619642b
8 changed files with 105 additions and 135 deletions

View File

@ -140,8 +140,7 @@ void CachedInterpreter::Jit(u32 address)
return;
}
int block_num = m_block_cache.AllocateBlock(PC);
JitBlock* b = m_block_cache.GetBlock(block_num);
JitBlock* b = m_block_cache.AllocateBlock(PC);
js.blockStart = PC;
js.firstFPInstructionFound = false;
@ -212,7 +211,7 @@ void CachedInterpreter::Jit(u32 address)
b->codeSize = (u32)(GetCodePtr() - b->checkedEntry);
b->originalSize = code_block.m_num_instructions;
m_block_cache.FinalizeBlock(block_num, jo.enableBlocklink, b->checkedEntry);
m_block_cache.FinalizeBlock(*b, jo.enableBlocklink, b->checkedEntry);
}
void CachedInterpreter::ClearCache()

View File

@ -589,9 +589,8 @@ void Jit64::Jit(u32 em_address)
return;
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock* b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, &code_buffer, b, nextPC));
JitBlock* b = blocks.AllocateBlock(em_address);
blocks.FinalizeBlock(*b, jo.enableBlocklink, DoJit(em_address, &code_buffer, b, nextPC));
}
const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBlock* b, u32 nextPC)

View File

@ -507,9 +507,8 @@ void JitIL::Jit(u32 em_address)
return;
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock* b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, &code_buffer, b, nextPC));
JitBlock* b = blocks.AllocateBlock(em_address);
blocks.FinalizeBlock(*b, jo.enableBlocklink, DoJit(em_address, &code_buffer, b, nextPC));
}
const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBlock* b, u32 nextPC)

View File

@ -398,10 +398,9 @@ void JitArm64::Jit(u32)
return;
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock* b = blocks.GetBlock(block_num);
JitBlock* b = blocks.AllocateBlock(em_address);
const u8* BlockPtr = DoJit(em_address, &code_buffer, b, nextPC);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
blocks.FinalizeBlock(*b, jo.enableBlocklink, BlockPtr);
}
const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBlock* b, u32 nextPC)

View File

@ -71,7 +71,7 @@ void JitBaseBlockCache::Clear()
m_jit.js.pairedQuantizeAddresses.clear();
for (int i = 1; i < num_blocks; i++)
{
DestroyBlock(i, false);
DestroyBlock(blocks[i], false);
}
links_to.clear();
block_map.clear();
@ -96,12 +96,7 @@ void JitBaseBlockCache::SchedulateClearCacheThreadSafe()
bool JitBaseBlockCache::IsFull() const
{
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
}
JitBlock* JitBaseBlockCache::GetBlock(int no)
{
return &blocks[no];
return num_blocks >= MAX_NUM_BLOCKS - 1;
}
JitBlock* JitBaseBlockCache::GetBlocks()
@ -109,17 +104,18 @@ JitBlock* JitBaseBlockCache::GetBlocks()
return blocks.data();
}
int JitBaseBlockCache::GetNumBlocks() const
{
return num_blocks;
}
int* JitBaseBlockCache::GetICache()
{
return iCache.data();
}
int JitBaseBlockCache::AllocateBlock(u32 em_address)
void JitBaseBlockCache::RunOnBlocks(std::function<void(const JitBlock&)> f)
{
for (int i = 0; i < num_blocks; i++)
f(blocks[i]);
}
JitBlock* JitBaseBlockCache::AllocateBlock(u32 em_address)
{
JitBlock& b = blocks[num_blocks];
b.invalid = false;
@ -128,48 +124,47 @@ int JitBaseBlockCache::AllocateBlock(u32 em_address)
b.msrBits = MSR & JitBlock::JIT_CACHE_MSR_MASK;
b.linkData.clear();
num_blocks++; // commit the current block
return num_blocks - 1;
return &b;
}
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8* code_ptr)
void JitBaseBlockCache::FinalizeBlock(JitBlock& block, bool block_link, const u8* code_ptr)
{
JitBlock& b = blocks[block_num];
if (start_block_map.count(b.physicalAddress))
if (start_block_map.count(block.physicalAddress))
{
// We already have a block at this address; invalidate the old block.
// This should be very rare. This will only happen if the same block
// is called both with DR/IR enabled or disabled.
WARN_LOG(DYNA_REC, "Invalidating compiled block at same address %08x", b.physicalAddress);
int old_block_num = start_block_map[b.physicalAddress];
const JitBlock& old_b = blocks[old_block_num];
WARN_LOG(DYNA_REC, "Invalidating compiled block at same address %08x", block.physicalAddress);
JitBlock& old_b = *start_block_map[block.physicalAddress];
block_map.erase(
std::make_pair(old_b.physicalAddress + 4 * old_b.originalSize - 1, old_b.physicalAddress));
DestroyBlock(old_block_num, true);
DestroyBlock(old_b, true);
}
start_block_map[b.physicalAddress] = block_num;
FastLookupEntryForAddress(b.effectiveAddress) = block_num;
const int block_num = static_cast<int>(&block - &blocks[0]);
start_block_map[block.physicalAddress] = &block;
FastLookupEntryForAddress(block.effectiveAddress) = block_num;
u32 pAddr = b.physicalAddress;
u32 pAddr = block.physicalAddress;
for (u32 block = pAddr / 32; block <= (pAddr + (b.originalSize - 1) * 4) / 32; ++block)
valid_block.Set(block);
for (u32 addr = pAddr / 32; addr <= (pAddr + (block.originalSize - 1) * 4) / 32; ++addr)
valid_block.Set(addr);
block_map[std::make_pair(pAddr + 4 * b.originalSize - 1, pAddr)] = block_num;
block_map[std::make_pair(pAddr + 4 * block.originalSize - 1, pAddr)] = &block;
if (block_link)
{
for (const auto& e : b.linkData)
for (const auto& e : block.linkData)
{
links_to.emplace(e.exitAddress, block_num);
links_to.emplace(e.exitAddress, &block);
}
LinkBlock(block_num);
LinkBlock(block);
}
JitRegister::Register(b.checkedEntry, b.codeSize, "JIT_PPC_%08x", b.physicalAddress);
JitRegister::Register(block.checkedEntry, block.codeSize, "JIT_PPC_%08x", block.physicalAddress);
}
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr, u32 msr)
JitBlock* JitBaseBlockCache::GetBlockFromStartAddress(u32 addr, u32 msr)
{
u32 translated_addr = addr;
if (UReg_MSR(msr).IR)
@ -177,23 +172,20 @@ int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr, u32 msr)
auto translated = PowerPC::JitCache_TranslateAddress(addr);
if (!translated.valid)
{
return -1;
return nullptr;
}
translated_addr = translated.address;
}
auto map_result = start_block_map.find(translated_addr);
if (map_result == start_block_map.end())
return -1;
int block_num = map_result->second;
const JitBlock& b = blocks[block_num];
if (b.invalid)
return -1;
if (b.effectiveAddress != addr)
return -1;
if (b.msrBits != (msr & JitBlock::JIT_CACHE_MSR_MASK))
return -1;
return block_num;
return nullptr;
JitBlock* b = map_result->second;
if (b->invalid || b->effectiveAddress != addr ||
b->msrBits != (msr & JitBlock::JIT_CACHE_MSR_MASK))
return nullptr;
return b;
}
const u8* JitBaseBlockCache::Dispatch()
@ -235,7 +227,7 @@ void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool for
auto it = block_map.lower_bound(std::make_pair(pAddr, 0));
while (it != block_map.end() && it->first.second < pAddr + length)
{
DestroyBlock(it->second, true);
DestroyBlock(*it->second, true);
it = block_map.erase(it);
}
@ -269,59 +261,53 @@ void JitBaseBlockCache::WriteDestroyBlock(const JitBlock& block)
// Can be faster by doing a queue for blocks to link up, and only process those
// Should probably be done
void JitBaseBlockCache::LinkBlockExits(int i)
void JitBaseBlockCache::LinkBlockExits(JitBlock& block)
{
JitBlock& b = blocks[i];
if (b.invalid)
if (block.invalid)
{
// This block is dead. Don't relink it.
return;
}
for (auto& e : b.linkData)
for (auto& e : block.linkData)
{
if (!e.linkStatus)
{
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress, b.msrBits);
if (destinationBlock != -1)
JitBlock* destinationBlock = GetBlockFromStartAddress(e.exitAddress, block.msrBits);
if (destinationBlock && !destinationBlock->invalid)
{
if (!blocks[destinationBlock].invalid)
{
WriteLinkBlock(e, &blocks[destinationBlock]);
e.linkStatus = true;
}
WriteLinkBlock(e, destinationBlock);
e.linkStatus = true;
}
}
}
}
void JitBaseBlockCache::LinkBlock(int i)
void JitBaseBlockCache::LinkBlock(JitBlock& block)
{
LinkBlockExits(i);
const JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.effectiveAddress);
LinkBlockExits(block);
auto ppp = links_to.equal_range(block.effectiveAddress);
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
const JitBlock& b2 = blocks[iter->second];
if (b.msrBits == b2.msrBits)
LinkBlockExits(iter->second);
JitBlock& b2 = *iter->second;
if (block.msrBits == b2.msrBits)
LinkBlockExits(b2);
}
}
void JitBaseBlockCache::UnlinkBlock(int i)
void JitBaseBlockCache::UnlinkBlock(const JitBlock& block)
{
JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.effectiveAddress);
auto ppp = links_to.equal_range(block.effectiveAddress);
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
JitBlock& sourceBlock = blocks[iter->second];
if (sourceBlock.msrBits != b.msrBits)
JitBlock& sourceBlock = *iter->second;
if (sourceBlock.msrBits != block.msrBits)
continue;
for (auto& e : sourceBlock.linkData)
{
if (e.exitAddress == b.effectiveAddress)
if (e.exitAddress == block.effectiveAddress)
{
WriteLinkBlock(e, nullptr);
e.linkStatus = false;
@ -330,51 +316,45 @@ void JitBaseBlockCache::UnlinkBlock(int i)
}
}
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
void JitBaseBlockCache::DestroyBlock(JitBlock& block, bool invalidate)
{
if (block_num < 0 || block_num >= num_blocks)
{
PanicAlert("DestroyBlock: Invalid block number %d", block_num);
return;
}
JitBlock& b = blocks[block_num];
if (b.invalid)
if (block.invalid)
{
if (invalidate)
PanicAlert("Invalidating invalid block %d", block_num);
PanicAlert("Invalidating invalid block %p", &block);
return;
}
b.invalid = true;
start_block_map.erase(b.physicalAddress);
FastLookupEntryForAddress(b.effectiveAddress) = 0;
block.invalid = true;
start_block_map.erase(block.physicalAddress);
FastLookupEntryForAddress(block.effectiveAddress) = 0;
UnlinkBlock(block_num);
UnlinkBlock(block);
// Delete linking addresses
auto it = links_to.equal_range(b.effectiveAddress);
auto it = links_to.equal_range(block.effectiveAddress);
while (it.first != it.second)
{
if (it.first->second == block_num)
if (it.first->second == &block)
it.first = links_to.erase(it.first);
else
it.first++;
}
// Raise an signal if we are going to call this block again
WriteDestroyBlock(b);
WriteDestroyBlock(block);
}
void JitBaseBlockCache::MoveBlockIntoFastCache(u32 addr, u32 msr)
{
int block_num = GetBlockNumberFromStartAddress(addr, msr);
if (block_num < 0)
JitBlock* block = GetBlockFromStartAddress(addr, msr);
if (!block)
{
Jit(addr);
}
else
{
FastLookupEntryForAddress(addr) = block_num;
LinkBlock(block_num);
FastLookupEntryForAddress(addr) = static_cast<int>(block - &blocks[0]);
LinkBlock(*block);
}
}

View File

@ -6,6 +6,7 @@
#include <array>
#include <bitset>
#include <functional>
#include <map>
#include <memory>
#include <vector>
@ -126,17 +127,17 @@ public:
bool IsFull() const;
// Code Cache
JitBlock* GetBlock(int block_num);
JitBlock* GetBlocks();
int GetNumBlocks() const;
int* GetICache();
void RunOnBlocks(std::function<void(const JitBlock&)> f);
int AllocateBlock(u32 em_address);
void FinalizeBlock(int block_num, bool block_link, const u8* code_ptr);
JitBlock* AllocateBlock(u32 em_address);
void FinalizeBlock(JitBlock& block, bool block_link, const u8* code_ptr);
// Look for the block in the slow but accurate way.
// This function shall be used if FastLookupEntryForAddress() failed.
int GetBlockNumberFromStartAddress(u32 em_address, u32 msr);
// This might return nullptr if there is no such block.
JitBlock* GetBlockFromStartAddress(u32 em_address, u32 msr);
// Get the normal entry for the block associated with the current program
// counter. This will JIT code if necessary. (This is the reference
@ -155,10 +156,10 @@ private:
virtual void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) = 0;
virtual void WriteDestroyBlock(const JitBlock& block);
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
void DestroyBlock(int block_num, bool invalidate);
void LinkBlockExits(JitBlock& block);
void LinkBlock(JitBlock& block);
void UnlinkBlock(const JitBlock& block);
void DestroyBlock(JitBlock& block, bool invalidate);
void MoveBlockIntoFastCache(u32 em_address, u32 msr);
@ -172,16 +173,16 @@ private:
// links_to hold all exit points of all valid blocks in a reverse way.
// It is used to query all blocks which links to an address.
std::multimap<u32, int> links_to; // destination_PC -> number
std::multimap<u32, JitBlock*> links_to; // destination_PC -> number
// Map indexed by the physical memory location.
// It is used to invalidate blocks based on memory location.
std::map<std::pair<u32, u32>, u32> block_map; // (end_addr, start_addr) -> number
std::map<std::pair<u32, u32>, JitBlock*> block_map; // (end_addr, start_addr) -> block
// Map indexed by the physical address of the entry point.
// This is used to query the block based on the current PC in a slow way.
// TODO: This is redundant with block_map, and both should be a multimap.
std::map<u32, u32> start_block_map; // start_addr -> number
std::map<u32, JitBlock*> start_block_map; // start_addr -> block
// This bitsets shows which cachelines overlap with any blocks.
// It is used to provide a fast way to query if no icache invalidation is needed.

View File

@ -134,26 +134,23 @@ void GetProfileResults(ProfileStats* prof_stats)
prof_stats->cost_sum = 0;
prof_stats->timecost_sum = 0;
prof_stats->block_stats.clear();
prof_stats->block_stats.reserve(g_jit->GetBlockCache()->GetNumBlocks());
Core::EState old_state = Core::GetState();
if (old_state == Core::CORE_RUN)
Core::SetState(Core::CORE_PAUSE);
QueryPerformanceFrequency((LARGE_INTEGER*)&prof_stats->countsPerSec);
for (int i = 0; i < g_jit->GetBlockCache()->GetNumBlocks(); i++)
{
const JitBlock* block = g_jit->GetBlockCache()->GetBlock(i);
g_jit->GetBlockCache()->RunOnBlocks([&prof_stats](const JitBlock& block) {
// Rough heuristic. Mem instructions should cost more.
u64 cost = block->originalSize * (block->runCount / 4);
u64 timecost = block->ticCounter;
u64 cost = block.originalSize * (block.runCount / 4);
u64 timecost = block.ticCounter;
// Todo: tweak.
if (block->runCount >= 1)
prof_stats->block_stats.emplace_back(i, block->effectiveAddress, cost, timecost,
block->runCount, block->codeSize);
if (block.runCount >= 1)
prof_stats->block_stats.emplace_back(block.effectiveAddress, cost, timecost, block.runCount,
block.codeSize);
prof_stats->cost_sum += cost;
prof_stats->timecost_sum += timecost;
}
});
sort(prof_stats->block_stats.begin(), prof_stats->block_stats.end());
if (old_state == Core::CORE_RUN)
@ -168,34 +165,31 @@ int GetHostCode(u32* address, const u8** code, u32* code_size)
return 1;
}
int block_num = g_jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address, MSR);
if (block_num < 0)
JitBlock* block = g_jit->GetBlockCache()->GetBlockFromStartAddress(*address, MSR);
if (!block)
{
for (int i = 0; i < 500; i++)
{
block_num = g_jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i, MSR);
if (block_num >= 0)
block = g_jit->GetBlockCache()->GetBlockFromStartAddress(*address - 4 * i, MSR);
if (block)
break;
}
if (block_num >= 0)
if (block)
{
JitBlock* block = g_jit->GetBlockCache()->GetBlock(block_num);
if (!(block->effectiveAddress <= *address &&
block->originalSize + block->effectiveAddress >= *address))
block_num = -1;
block = nullptr;
}
// Do not merge this "if" with the above - block_num changes inside it.
if (block_num < 0)
if (!block)
{
*code_size = 0;
return 2;
}
}
JitBlock* block = g_jit->GetBlockCache()->GetBlock(block_num);
*code = block->checkedEntry;
*code_size = block->codeSize;
*address = block->effectiveAddress;

View File

@ -43,11 +43,10 @@
struct BlockStat
{
BlockStat(int bn, u32 _addr, u64 c, u64 ticks, u64 run, u32 size)
: blockNum(bn), addr(_addr), cost(c), tick_counter(ticks), run_count(run), block_size(size)
BlockStat(u32 _addr, u64 c, u64 ticks, u64 run, u32 size)
: addr(_addr), cost(c), tick_counter(ticks), run_count(run), block_size(size)
{
}
int blockNum;
u32 addr;
u64 cost;
u64 tick_counter;