Skip to content

Commit

Permalink
PPU/LLVM: Do not recompile blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
vlj authored and Nekotekina committed Sep 1, 2015
1 parent 665f316 commit f2c8db7
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 119 deletions.
117 changes: 15 additions & 102 deletions rpcs3/Emu/Cell/PPULLVMRecompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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))
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 2 additions & 17 deletions rpcs3/Emu/Cell/PPULLVMRecompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;

Expand All @@ -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 {
Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand Down

0 comments on commit f2c8db7

Please sign in to comment.