mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
PPU/LLVM: Do not recompile blocks
This commit is contained in:
parent
665f316a3b
commit
f2c8db75bf
@ -318,15 +318,7 @@ const Executable *RecompilationEngine::GetExecutable(u32 address, bool isFunctio
|
||||
return isFunction ? &executeFunc : &executeUntilReturn;
|
||||
}
|
||||
|
||||
std::pair<std::mutex, std::atomic<int> >* RecompilationEngine::GetMutexAndCounterForAddress(u32 address) {
|
||||
std::lock_guard<std::mutex> lock(m_address_locks_lock);
|
||||
std::unordered_map<u32, std::pair<std::mutex, std::atomic<int>> >::iterator It = m_address_locks.find(address);
|
||||
if (It == m_address_locks.end())
|
||||
return nullptr;
|
||||
return &(It->second);
|
||||
}
|
||||
|
||||
bool RecompilationEngine::isAddressCommited(u32 address) const
|
||||
bool RecompilationEngine::isAddressCommited(u32 address) const
|
||||
{
|
||||
size_t offset = address * sizeof(Executable);
|
||||
size_t page = offset / 4096;
|
||||
@ -345,7 +337,7 @@ void RecompilationEngine::commitAddress(u32 address)
|
||||
FunctionCachePagesCommited[page >> 3] |= (1 << (page & 7));
|
||||
}
|
||||
|
||||
const Executable RecompilationEngine::GetCompiledExecutableIfAvailable(u32 address)
|
||||
const Executable RecompilationEngine::GetCompiledExecutableIfAvailable(u32 address)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_address_to_function_lock);
|
||||
if (!isAddressCommited(address / 4))
|
||||
@ -361,22 +353,6 @@ const Executable RecompilationEngine::GetCompiledExecutableIfAvailable(u32 addre
|
||||
return std::get<0>(It->second);
|
||||
}
|
||||
|
||||
void RecompilationEngine::RemoveUnusedEntriesFromCache() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_cache_clear_time).count() > 10000) {
|
||||
for (auto i = m_address_to_function.begin(); i != m_address_to_function.end();) {
|
||||
auto tmp = i;
|
||||
i++;
|
||||
if (std::get<2>(tmp->second) == 0)
|
||||
m_address_to_function.erase(tmp);
|
||||
else
|
||||
std::get<2>(tmp->second) = 0;
|
||||
}
|
||||
|
||||
m_last_cache_clear_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
void RecompilationEngine::NotifyTrace(ExecutionTrace * execution_trace) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_pending_execution_traces_lock);
|
||||
@ -402,7 +378,6 @@ raw_fd_ostream & RecompilationEngine::Log() {
|
||||
}
|
||||
|
||||
void RecompilationEngine::Task() {
|
||||
bool is_idling = false;
|
||||
std::chrono::nanoseconds idling_time(0);
|
||||
std::chrono::nanoseconds recompiling_time(0);
|
||||
|
||||
@ -423,46 +398,11 @@ void RecompilationEngine::Task() {
|
||||
|
||||
if (execution_trace) {
|
||||
ProcessExecutionTrace(*execution_trace);
|
||||
delete execution_trace;
|
||||
work_done_this_iteration = true;
|
||||
delete execution_trace;
|
||||
}
|
||||
|
||||
if (!work_done_this_iteration) {
|
||||
// TODO: Reduce the priority of the recompilation engine thread if its set to high priority
|
||||
}
|
||||
else {
|
||||
is_idling = false;
|
||||
}
|
||||
|
||||
if (is_idling) {
|
||||
auto recompiling_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Recompile the function whose CFG has changed the most since the last time it was compiled
|
||||
auto candidate = (BlockEntry *)nullptr;
|
||||
size_t max_diff = 0;
|
||||
for (auto block : m_block_table) {
|
||||
if (block->IsFunction() && block->is_compiled) {
|
||||
auto diff = block->cfg.GetSize() - block->last_compiled_cfg_size;
|
||||
if (diff > max_diff) {
|
||||
candidate = block;
|
||||
max_diff = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate != nullptr) {
|
||||
Log() << "Recompiling: " << candidate->ToString() << "\n";
|
||||
CompileBlock(*candidate);
|
||||
work_done_this_iteration = true;
|
||||
}
|
||||
|
||||
auto recompiling_end = std::chrono::high_resolution_clock::now();
|
||||
recompiling_time += std::chrono::duration_cast<std::chrono::nanoseconds>(recompiling_end - recompiling_start);
|
||||
}
|
||||
|
||||
if (!work_done_this_iteration) {
|
||||
is_idling = true;
|
||||
|
||||
// Wait a few ms for something to happen
|
||||
auto idling_start = std::chrono::high_resolution_clock::now();
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
@ -583,8 +523,8 @@ void RecompilationEngine::CompileBlock(BlockEntry & block_entry) {
|
||||
Log() << "Compile: " << block_entry.ToString() << "\n";
|
||||
Log() << "CFG: " << block_entry.cfg.ToString() << "\n";
|
||||
|
||||
const std::pair<Executable, llvm::ExecutionEngine *> &compileResult =
|
||||
m_compiler.Compile(fmt::format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), block_entry.cfg,
|
||||
const std::pair<Executable, llvm::ExecutionEngine *> &compileResult =
|
||||
m_compiler.Compile(fmt::format("fn_0x%08X", block_entry.cfg.start_address), block_entry.cfg,
|
||||
block_entry.IsFunction() ? true : false /*generate_linkable_exits*/);
|
||||
|
||||
// If entry doesn't exist, create it (using lock)
|
||||
@ -597,24 +537,6 @@ void RecompilationEngine::CompileBlock(BlockEntry & block_entry) {
|
||||
commitAddress(block_entry.cfg.start_address / 4);
|
||||
}
|
||||
|
||||
std::unordered_map<u32, std::pair<std::mutex, std::atomic<int>> >::iterator It2 = m_address_locks.find(block_entry.cfg.start_address);
|
||||
if (It2 == m_address_locks.end())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_address_locks_lock);
|
||||
(void)m_address_locks[block_entry.cfg.start_address];
|
||||
m_address_locks[block_entry.cfg.start_address].second.store(0);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_address_locks[block_entry.cfg.start_address].first);
|
||||
|
||||
int loopiteration = 0;
|
||||
while (m_address_locks[block_entry.cfg.start_address].second.load() > 0)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
if (loopiteration++ > 10000000)
|
||||
return;
|
||||
}
|
||||
|
||||
std::get<1>(m_address_to_function[block_entry.cfg.start_address]) = std::unique_ptr<llvm::ExecutionEngine>(compileResult.second);
|
||||
std::get<0>(m_address_to_function[block_entry.cfg.start_address]) = compileResult.first;
|
||||
std::get<3>(m_address_to_function[block_entry.cfg.start_address]) = m_currentId;
|
||||
@ -768,25 +690,16 @@ u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteTillReturn(PPUThread
|
||||
if (context)
|
||||
execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledFunction, context >> 32, context & 0xFFFFFFFF);
|
||||
|
||||
while (PollStatus(ppu_state) == false) {
|
||||
std::pair<std::mutex, std::atomic<int>> *mut = execution_engine->m_recompilation_engine->GetMutexAndCounterForAddress(ppu_state->PC);
|
||||
if (mut) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mut->first);
|
||||
mut->second.fetch_add(1);
|
||||
}
|
||||
const Executable executable = execution_engine->m_recompilation_engine->GetCompiledExecutableIfAvailable(ppu_state->PC);
|
||||
if (executable)
|
||||
{
|
||||
auto entry = ppu_state->PC;
|
||||
u32 exit = (u32)executable(ppu_state, 0);
|
||||
mut->second.fetch_sub(1);
|
||||
execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledBlock, entry, exit);
|
||||
if (exit == 0)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
mut->second.fetch_add(1);
|
||||
while (PollStatus(ppu_state) == false) {
|
||||
const Executable executable = execution_engine->m_recompilation_engine->GetCompiledExecutableIfAvailable(ppu_state->PC);
|
||||
if (executable)
|
||||
{
|
||||
auto entry = ppu_state->PC;
|
||||
u32 exit = (u32)executable(ppu_state, 0);
|
||||
execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledBlock, entry, exit);
|
||||
if (exit == 0)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
execution_engine->m_tracer.Trace(Tracer::TraceType::Instruction, ppu_state->PC, 0);
|
||||
u32 instruction = vm::ps3::read32(ppu_state->PC);
|
||||
|
@ -1015,11 +1015,6 @@ namespace ppu_recompiler_llvm {
|
||||
**/
|
||||
const Executable *GetExecutable(u32 address, bool isFunction);
|
||||
|
||||
/**
|
||||
* Get a mutex for an address. Used to avoid modifying a block currently in execution.
|
||||
**/
|
||||
std::pair<std::mutex, std::atomic<int> >* GetMutexAndCounterForAddress(u32 address);
|
||||
|
||||
/**
|
||||
* Get the executable for the specified address if a compiled version is
|
||||
* available, otherwise returns nullptr.
|
||||
@ -1043,9 +1038,6 @@ namespace ppu_recompiler_llvm {
|
||||
/// Number of times this block was hit
|
||||
u32 num_hits;
|
||||
|
||||
/// The current revision number of this function
|
||||
u32 revision;
|
||||
|
||||
/// Size of the CFG when it was last compiled
|
||||
size_t last_compiled_cfg_size;
|
||||
|
||||
@ -1057,15 +1049,14 @@ namespace ppu_recompiler_llvm {
|
||||
|
||||
BlockEntry(u32 start_address, u32 function_address)
|
||||
: num_hits(0)
|
||||
, revision(0)
|
||||
, last_compiled_cfg_size(0)
|
||||
, is_compiled(false)
|
||||
, cfg(start_address, function_address) {
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
return fmt::format("0x%08X (0x%08X): NumHits=%u, Revision=%u, LastCompiledCfgSize=%u, IsCompiled=%c",
|
||||
cfg.start_address, cfg.function_address, num_hits, revision, last_compiled_cfg_size, is_compiled ? 'Y' : 'N');
|
||||
return fmt::format("0x%08X (0x%08X): NumHits=%u, LastCompiledCfgSize=%u, IsCompiled=%c",
|
||||
cfg.start_address, cfg.function_address, num_hits, last_compiled_cfg_size, is_compiled ? 'Y' : 'N');
|
||||
}
|
||||
|
||||
bool operator == (const BlockEntry & other) const {
|
||||
@ -1106,8 +1097,6 @@ namespace ppu_recompiler_llvm {
|
||||
|
||||
/// Lock for accessing m_address_to_function.
|
||||
std::mutex m_address_to_function_lock;
|
||||
/// Lock for modifying address mutex table
|
||||
std::mutex m_address_locks_lock;
|
||||
|
||||
int m_currentId;
|
||||
|
||||
@ -1126,14 +1115,10 @@ namespace ppu_recompiler_llvm {
|
||||
typedef std::tuple<Executable, std::unique_ptr<llvm::ExecutionEngine>, u32, u32> ExecutableStorage;
|
||||
/// Address to ordinal cahce. Key is address.
|
||||
std::unordered_map<u32, ExecutableStorage> m_address_to_function;
|
||||
std::unordered_map<u32, std::pair<std::mutex, std::atomic<int> > > m_address_locks;
|
||||
|
||||
/// The time at which the m_address_to_ordinal cache was last cleared
|
||||
std::chrono::high_resolution_clock::time_point m_last_cache_clear_time;
|
||||
|
||||
/// Remove unused entries from the m_address_to_ordinal cache
|
||||
void RemoveUnusedEntriesFromCache();
|
||||
|
||||
/// PPU Compiler
|
||||
Compiler m_compiler;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user