Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Source/Core/Core/PowerPC/StaticRecomp/StaticRecompCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,35 @@ void StaticRecompCore::LoadModule()
desc->num_smc_ranges);
}


bool StaticRecompCore::IsBusyWaitLoop(u32 address)
{
// Analysis reads guest memory and is not meaningful while a debugger or
// branch watch is active, both of which want every block to actually execute.
if (IsDebuggingEnabled() || IsBranchWatchEnabled())
return false;

const auto cached = m_busy_wait_cache.find(address);
if (cached != m_busy_wait_cache.end())
return cached->second;

constexpr std::size_t max_loop_instructions = 64;
analyzer.Analyze(address, &code_block, &m_code_buffer,
std::min(max_loop_instructions, m_code_buffer.size()));

const bool is_busy_wait = !code_block.m_memory_exception &&
std::any_of(m_code_buffer.begin(),
m_code_buffer.begin() + code_block.m_num_instructions,
[address](const PPCAnalyst::CodeOp& op) {
return op.branchIsIdleLoop && op.branchTo == address;
});
m_busy_wait_cache.emplace(address, is_busy_wait);
return is_busy_wait;
}

void StaticRecompCore::ClearCache()
{
m_busy_wait_cache.clear();
if (m_fallback_jit)
m_fallback_jit->ClearCache();

Expand Down
8 changes: 8 additions & 0 deletions Source/Core/Core/PowerPC/StaticRecomp/StaticRecompCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class StaticRecompCore : public JitBase
bool IsHostCallAddress(u32 address) const;
bool ShouldYieldAt(u32 address);

// True when `address` is the head of a guest busy-wait loop, so the
// dispatcher can hand the remaining slice back to CoreTiming instead of
// spinning through it in real time.
bool IsBusyWaitLoop(u32 address);

void ClearCache() override;
void Jit(u32 em_address) override {}
bool HandleFault(uintptr_t access_address, SContext* ctx) override { return false; }
Expand Down Expand Up @@ -175,6 +180,9 @@ class StaticRecompCore : public JitBase
u64 m_hook_fallback_instructions = 0;
u64 m_timebase_cycle_remainder = 0;
std::unordered_map<u32, u64> m_dispatch_samples;
// Analysis result per loop-head address. Invalidated whenever guest code
// could have changed (ClearCache, SMC re-verification, REL relink).
std::unordered_map<u32, bool> m_busy_wait_cache;
u64 m_bursts = 0; // SyncIn..SyncOut native runs (diagnostic)
u64 m_charged_cycles = 0; // cycles flushed from module charges (diagnostic)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,14 @@ void StaticRecompCore::Run()
m_timebase_cycle_remainder = total_cycles % SystemTimers::TIMER_RATIO;

// Idle loop skipping for configured target loops (e.g. Wii Menu OSIdleThread)
if (m_guest.pc == m_idle_pc && m_idle_pc != 0)
// A configured idle PC only covers loops we already know about. A
// dispatch that returns to the address it entered at is a self-loop,
// and if the analyzer agrees it is an idle loop there is nothing to
// gain from spinning it in real time.
const bool configured_idle = m_idle_pc != 0 && m_guest.pc == m_idle_pc;
const bool detected_idle = m_guest.pc == runtime_dispatch_address &&
IsBusyWaitLoop(runtime_dispatch_address);
if (configured_idle || detected_idle)
{
m_system.GetCoreTiming().Idle();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void StaticRecompCore::RefreshRelSections()
if (m_chunk_state[i] == CHUNK_FAILED && m_failed_chunks != 0)
--m_failed_chunks;
m_chunk_state[i] = CHUNK_UNVERIFIED;
m_busy_wait_cache.clear();
m_effective_chunk_hashes[i] = m_module->chunk_hashes[i];
}
}
Expand Down