Revert "JitCache: Support for VMEM + MSR bits"

This commit is contained in:
Scott Mansell
2016-07-27 11:15:25 +12:00
committed by GitHub
parent d7de39ebbe
commit 6834b4cb27
19 changed files with 369 additions and 447 deletions

View File

@ -55,10 +55,6 @@
#define JITDISABLE(setting) \
FALLBACK_IF(SConfig::GetInstance().bJITOff || SConfig::GetInstance().setting)
class JitBase;
extern JitBase* jit;
class JitBase : public CPUCoreBase
{
protected:
@ -129,7 +125,6 @@ public:
JitOptions jo;
JitState js;
static const u8* Dispatch() { return jit->GetBlockCache()->Dispatch(); };
virtual JitBaseBlockCache* GetBlockCache() = 0;
virtual void Jit(u32 em_address) = 0;
@ -152,6 +147,8 @@ public:
bool HandleFault(uintptr_t access_address, SContext* ctx) override;
};
extern JitBase* jit;
void Jit(u32 em_address);
// Merged routines that should be moved somewhere better

View File

@ -34,15 +34,26 @@ bool JitBaseBlockCache::IsFull() const
void JitBaseBlockCache::Init()
{
if (m_initialized)
{
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
return;
}
JitRegister::Init(SConfig::GetInstance().m_perfDir);
iCache.fill(0);
iCache.fill(JIT_ICACHE_INVALID_BYTE);
iCacheEx.fill(JIT_ICACHE_INVALID_BYTE);
iCacheVMEM.fill(JIT_ICACHE_INVALID_BYTE);
Clear();
m_initialized = true;
}
void JitBaseBlockCache::Shutdown()
{
num_blocks = 0;
m_initialized = false;
JitRegister::Shutdown();
}
@ -69,8 +80,7 @@ void JitBaseBlockCache::Clear()
valid_block.ClearAll();
num_blocks = 0;
blocks[0].msrBits = 0xFFFFFFFF;
blocks[0].invalid = true;
blockCodePointers.fill(nullptr);
}
void JitBaseBlockCache::Reset()
@ -93,9 +103,7 @@ int JitBaseBlockCache::AllocateBlock(u32 em_address)
{
JitBlock& b = blocks[num_blocks];
b.invalid = false;
b.effectiveAddress = em_address;
b.physicalAddress = PowerPC::JitCache_TranslateAddress(em_address).address;
b.msrBits = MSR & JitBlock::JIT_CACHE_MSR_MASK;
b.originalAddress = em_address;
b.linkData.clear();
num_blocks++; // commit the current block
return num_blocks - 1;
@ -103,23 +111,13 @@ int JitBaseBlockCache::AllocateBlock(u32 em_address)
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8* code_ptr)
{
blockCodePointers[block_num] = code_ptr;
JitBlock& b = blocks[block_num];
if (start_block_map.count(b.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];
block_map.erase(
std::make_pair(old_b.physicalAddress + 4 * old_b.originalSize - 1, old_b.physicalAddress));
DestroyBlock(old_block_num, true);
}
start_block_map[b.physicalAddress] = block_num;
FastLookupEntryForAddress(b.effectiveAddress) = block_num;
u32 pAddr = b.physicalAddress;
std::memcpy(GetICachePtr(b.originalAddress), &block_num, sizeof(u32));
// Convert the logical address to a physical address for the block map
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
for (u32 block = pAddr / 32; block <= (pAddr + (b.originalSize - 1) * 4) / 32; ++block)
valid_block.Set(block);
@ -134,64 +132,49 @@ void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8*
}
LinkBlock(block_num);
LinkBlockExits(block_num);
}
JitRegister::Register(b.checkedEntry, b.codeSize, "JIT_PPC_%08x", b.physicalAddress);
JitRegister::Register(blockCodePointers[block_num], b.codeSize, "JIT_PPC_%08x",
b.originalAddress);
}
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr, u32 msr)
const u8** JitBaseBlockCache::GetCodePointers()
{
u32 translated_addr = addr;
if (UReg_MSR(msr).IR)
{
auto translated = PowerPC::JitCache_TranslateAddress(addr);
if (!translated.valid)
{
return -1;
}
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 blockCodePointers.data();
}
void JitBaseBlockCache::MoveBlockIntoFastCache(u32 addr, u32 msr)
u8* JitBaseBlockCache::GetICachePtr(u32 addr)
{
int block_num = GetBlockNumberFromStartAddress(addr, msr);
if (block_num < 0)
{
Jit(addr);
}
else
{
FastLookupEntryForAddress(addr) = block_num;
LinkBlock(block_num);
}
if (addr & JIT_ICACHE_VMEM_BIT)
return &jit->GetBlockCache()->iCacheVMEM[addr & JIT_ICACHE_MASK];
if (addr & JIT_ICACHE_EXRAM_BIT)
return &jit->GetBlockCache()->iCacheEx[addr & JIT_ICACHEEX_MASK];
return &jit->GetBlockCache()->iCache[addr & JIT_ICACHE_MASK];
}
const u8* JitBaseBlockCache::Dispatch()
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
{
int block_num = FastLookupEntryForAddress(PC);
u32 inst;
std::memcpy(&inst, GetICachePtr(addr), sizeof(u32));
while (blocks[block_num].effectiveAddress != PC ||
blocks[block_num].msrBits != (MSR & JitBlock::JIT_CACHE_MSR_MASK))
{
MoveBlockIntoFastCache(PC, MSR & JitBlock::JIT_CACHE_MSR_MASK);
block_num = FastLookupEntryForAddress(PC);
}
if (inst & 0xfc000000) // definitely not a JIT block
return -1;
return blocks[block_num].normalEntry;
if ((int)inst >= num_blocks)
return -1;
if (blocks[inst].originalAddress != addr)
return -1;
return inst;
}
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
{
return (CompiledCode)blockCodePointers[block_num];
}
// Block linker
@ -212,14 +195,11 @@ void JitBaseBlockCache::LinkBlockExits(int i)
{
if (!e.linkStatus)
{
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress, b.msrBits);
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress);
if (destinationBlock != -1)
{
if (!blocks[destinationBlock].invalid)
{
WriteLinkBlock(e, &blocks[destinationBlock]);
e.linkStatus = true;
}
WriteLinkBlock(e.exitPtrs, blocks[destinationBlock]);
e.linkStatus = true;
}
}
}
@ -228,37 +208,39 @@ void JitBaseBlockCache::LinkBlockExits(int i)
void JitBaseBlockCache::LinkBlock(int i)
{
LinkBlockExits(i);
const JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.effectiveAddress);
JitBlock& b = blocks[i];
// equal_range(b) returns pair<iterator,iterator> representing the range
// of element with key b
auto ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
const JitBlock& b2 = blocks[iter->second];
if (b.msrBits == b2.msrBits)
LinkBlockExits(iter->second);
// PanicAlert("Linking block %i to block %i", iter->second, i);
LinkBlockExits(iter->second);
}
}
void JitBaseBlockCache::UnlinkBlock(int i)
{
JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.effectiveAddress);
auto ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
JitBlock& sourceBlock = blocks[iter->second];
if (sourceBlock.msrBits != b.msrBits)
continue;
for (auto& e : sourceBlock.linkData)
{
if (e.exitAddress == b.effectiveAddress)
{
WriteLinkBlock(e, nullptr);
if (e.exitAddress == b.originalAddress)
e.linkStatus = false;
}
}
}
links_to.erase(b.originalAddress);
}
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
@ -276,31 +258,20 @@ void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
return;
}
b.invalid = true;
start_block_map.erase(b.physicalAddress);
FastLookupEntryForAddress(b.effectiveAddress) = 0;
std::memcpy(GetICachePtr(b.originalAddress), &JIT_ICACHE_INVALID_WORD, sizeof(u32));
UnlinkBlock(block_num);
// Delete linking adresses
auto it = links_to.equal_range(b.effectiveAddress);
while (it.first != it.second)
{
if (it.first->second == block_num)
it.first = links_to.erase(it.first);
else
it.first++;
}
// Raise an signal if we are going to call this block again
WriteDestroyBlock(b);
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
}
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool forced)
{
auto translated = PowerPC::JitCache_TranslateAddress(address);
if (!translated.valid)
return;
u32 pAddr = translated.address;
// Convert the logical address to a physical address for the block map
u32 pAddr = address & 0x1FFFFFFF;
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
bool destroy_block = true;
@ -317,11 +288,20 @@ void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool for
// address
if (destroy_block)
{
auto it = block_map.lower_bound(std::make_pair(pAddr, 0));
while (it != block_map.end() && it->first.second < pAddr + length)
std::map<std::pair<u32, u32>, u32>::iterator it1 = block_map.lower_bound(
std::make_pair(pAddr, 0)),
it2 = it1;
while (it2 != block_map.end() && it2->first.second < pAddr + length)
{
DestroyBlock(it->second, true);
it = block_map.erase(it);
JitBlock& b = blocks[it2->second];
std::memcpy(GetICachePtr(b.originalAddress), &JIT_ICACHE_INVALID_WORD, sizeof(u32));
DestroyBlock(it2->second, true);
++it2;
}
if (it1 != it2)
{
block_map.erase(it1, it2);
}
// If the code was actually modified, we need to clear the relevant entries from the
@ -339,10 +319,9 @@ void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool for
}
}
void JitBlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
void JitBlockCache::WriteLinkBlock(u8* location, const JitBlock& block)
{
u8* location = source.exitPtrs;
const u8* address = dest ? dest->checkedEntry : jit->GetAsmRoutines()->dispatcher;
const u8* address = block.checkedEntry;
XEmitter emit(location);
if (*location == 0xE8)
{
@ -361,11 +340,9 @@ void JitBlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBl
}
}
void JitBlockCache::WriteDestroyBlock(const JitBlock& block)
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
{
// Only clear the entry points as we might still be within this block.
XEmitter emit((u8*)block.checkedEntry);
emit.INT3();
XEmitter emit2((u8*)block.normalEntry);
emit2.INT3();
XEmitter emit((u8*)location);
emit.MOV(32, PPCSTATE(pc), Imm32(address));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
}

View File

@ -12,55 +12,32 @@
#include "Common/CommonTypes.h"
// A JitBlock is block of compiled code which corresponds to the PowerPC
// code at a given address.
//
// The notion of the address of a block is a bit complicated because of the
// way address translation works, but basically it's the combination of an
// effective address, the address translation bits in MSR, and the physical
// address.
static const u32 JIT_ICACHE_SIZE = 0x2000000;
static const u32 JIT_ICACHE_MASK = 0x1ffffff;
static const u32 JIT_ICACHEEX_SIZE = 0x4000000;
static const u32 JIT_ICACHEEX_MASK = 0x3ffffff;
static const u32 JIT_ICACHE_EXRAM_BIT = 0x10000000;
static const u32 JIT_ICACHE_VMEM_BIT = 0x20000000;
// This corresponds to opcode 5 which is invalid in PowerPC
static const u32 JIT_ICACHE_INVALID_BYTE = 0x80;
static const u32 JIT_ICACHE_INVALID_WORD = 0x80808080;
struct JitBlock
{
enum
{
// Mask for the MSR bits which determine whether a compiled block
// is valid (MSR.IR and MSR.DR, the address translation bits).
JIT_CACHE_MSR_MASK = 0x30,
};
// A special entry point for block linking; usually used to check the
// downcount.
const u8* checkedEntry;
// The normal entry point for the block, returned by Dispatch().
const u8* normalEntry;
// The effective address (PC) for the beginning of the block.
u32 effectiveAddress;
// The MSR bits expected for this block to be valid; see JIT_CACHE_MSR_MASK.
u32 msrBits;
// The physical address of the code represented by this block.
// Various maps in the cache are indexed by this (start_block_map,
// block_map, and valid_block in particular). This is useful because of
// of the way the instruction cache works on PowerPC.
u32 physicalAddress;
// The number of bytes of JIT'ed code contained in this block. Mostly
// useful for logging.
u32 originalAddress;
u32 codeSize;
// The number of PPC instructions represented by this block. Mostly
// useful for logging.
u32 originalSize;
int runCount; // for profiling.
// Whether this struct refers to a valid block. This is mostly useful as
// a debugging aid.
// FIXME: Change current users of invalid bit to assertions?
bool invalid;
// Information about exits to a known address from this block.
// This is used to implement block linking.
struct LinkData
{
u8* exitPtrs; // to be able to rewrite the exit jump
u8* exitPtrs; // to be able to rewrite the exit jum
u32 exitAddress;
bool linkStatus; // is it already linked?
};
@ -82,12 +59,7 @@ class ValidBlockBitSet final
public:
enum
{
// ValidBlockBitSet covers the whole 32-bit address-space in 32-byte
// chunks.
// FIXME: Maybe we can get away with less? There isn't any actual
// RAM in most of this space.
VALID_BLOCK_MASK_SIZE = (1ULL << 32) / 32,
// The number of elements in the allocated array. Each u32 contains 32 bits.
VALID_BLOCK_MASK_SIZE = 0x20000000 / 32,
VALID_BLOCK_ALLOC_ELEMENTS = VALID_BLOCK_MASK_SIZE / 32
};
// Directly accessed by Jit64.
@ -107,52 +79,33 @@ public:
class JitBaseBlockCache
{
public:
static constexpr int MAX_NUM_BLOCKS = 65536 * 2;
static constexpr u32 iCache_Num_Elements = 0x10000;
static constexpr u32 iCache_Mask = iCache_Num_Elements - 1;
enum
{
MAX_NUM_BLOCKS = 65536 * 2,
};
private:
// We store the metadata of all blocks in a linear way within this array.
std::array<JitBlock, MAX_NUM_BLOCKS> blocks; // number -> JitBlock
std::array<const u8*, MAX_NUM_BLOCKS> blockCodePointers;
std::array<JitBlock, MAX_NUM_BLOCKS> blocks;
int num_blocks;
// 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
// Map indexed by the physical memory location.
// It is used to invalidate blocks based on memory location.
std::multimap<u32, int> links_to;
std::map<std::pair<u32, u32>, u32> block_map; // (end_addr, start_addr) -> number
// 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
// 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.
ValidBlockBitSet valid_block;
// This array is indexed with the masked PC and likely holds the correct block id.
// This is used as a fast cache of start_block_map used in the assembly dispatcher.
std::array<int, iCache_Num_Elements> iCache; // start_addr & mask -> number
bool m_initialized;
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
u8* GetICachePtr(u32 addr);
void DestroyBlock(int block_num, bool invalidate);
void MoveBlockIntoFastCache(u32 em_address, u32 msr);
// Fast but risky block lookup based on iCache.
int& FastLookupEntryForAddress(u32 address) { return iCache[(address >> 2) & iCache_Mask]; }
// Virtual for overloaded
virtual void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) = 0;
virtual void WriteDestroyBlock(const JitBlock& block) {}
virtual void WriteLinkBlock(u8* location, const JitBlock& block) = 0;
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;
public:
JitBaseBlockCache() : num_blocks(0) {}
JitBaseBlockCache() : num_blocks(0), m_initialized(false) {}
virtual ~JitBaseBlockCache() {}
int AllocateBlock(u32 em_address);
void FinalizeBlock(int block_num, bool block_link, const u8* code_ptr);
@ -166,20 +119,18 @@ public:
// Code Cache
JitBlock* GetBlock(int block_num);
JitBlock* GetBlocks() { return blocks.data(); }
int* GetICache() { return iCache.data(); }
int GetNumBlocks() const;
const u8** GetCodePointers();
std::array<u8, JIT_ICACHE_SIZE> iCache;
std::array<u8, JIT_ICACHEEX_SIZE> iCacheEx;
std::array<u8, JIT_ICACHE_SIZE> iCacheVMEM;
// 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);
// Fast way to get a block. Only works on the first ppc instruction of a block.
int GetBlockNumberFromStartAddress(u32 em_address);
// Get the normal entry for the block associated with the current program
// counter. This will JIT code if necessary. (This is the reference
// implementation; high-performance JITs will want to use a custom
// assembly version.)
const u8* Dispatch();
CompiledCode GetCompiledCodeFromBlock(int block_num);
// DOES NOT WORK CORRECTLY WITH INLINING
void InvalidateICache(u32 address, const u32 length, bool forced);
u32* GetBlockBitSet() const { return valid_block.m_valid_block.get(); }
@ -189,6 +140,6 @@ public:
class JitBlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override;
void WriteDestroyBlock(const JitBlock& block) override;
void WriteLinkBlock(u8* location, const JitBlock& block) override;
void WriteDestroyBlock(const u8* location, u32 address) override;
};