diff --git a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.cpp b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.cpp index da9fb99f8c..0a29208b13 100644 --- a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.cpp +++ b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.cpp @@ -35,9 +35,12 @@ StaticRecompLockstepVerifier::~StaticRecompLockstepVerifier() { std::fprintf(stderr, "[lockstep] summary: checks=%llu reports=%llu skipped_fallback=%llu " - "skipped_zero=%llu undercharges=%llu max_deficit=%lld distinct_pcs=%zu\n", + "skipped_zero=%llu cap_hits=%llu undercharges=%llu " + "max_deficit=%lld distinct_pcs=%zu +", (unsigned long long)m_ls_checks, (unsigned long long)m_ls_reports, (unsigned long long)m_ls_skipped_fallback, (unsigned long long)m_ls_skipped_zero, + (unsigned long long)m_ls_cap_hits, (unsigned long long)m_ls_undercharges, (long long)m_ls_max_undercharge, m_ls_checked.size()); if (m_set_mem_journal) diff --git a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.h b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.h index 3e8a0ee9db..bba96f7877 100644 --- a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.h +++ b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep.h @@ -145,7 +145,15 @@ class StaticRecompLockstepVerifier u64 m_ls_start = 0; // begin checking at this native-dispatch index u64 m_ls_limit = 0; // stop checking after this index (0 = no bound) u64 m_ls_max_report = 0; // cap divergence reports (0 = unlimited) - int m_ls_step_cap = 512; // interpreter single-steps before giving up on end PC + // Interpreter single-steps before giving up on end PC. 512 is far too low, + // and it makes a default run misleading in BOTH directions: it manufactures + // CTRLFLOW "divergences" that are only the shadow running out of steps, and + // it hides real register divergences in every block longer than the cap, + // because the walk stops before it ever reaches the comparison. Measured on + // a full retail title, raising it to 20000 took CTRLFLOW reports 631 -> 22 + // and FP divergences 463 -> 622 -- 159 real reports the old default hid. + int m_ls_step_cap = 20000; + u64 m_ls_cap_hits = 0; // gave up on end PC at the step cap -- NOT a divergence u64 m_ls_checks = 0; // distinct blocks differentially checked u64 m_ls_reports = 0; // divergences reported u64 m_ls_skipped_fallback = 0; // blocks skipped (native used instruction fallback) diff --git a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep_Check.cpp b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep_Check.cpp index 6e72cb5eab..2ec5280299 100644 --- a/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep_Check.cpp +++ b/Source/Core/Core/PowerPC/StaticRecomp/StaticRecompLockstep_Check.cpp @@ -194,6 +194,18 @@ void StaticRecompLockstepVerifier::LockstepCheck(u32 entry_pc, u32 end_pc, const StaticRecompLockstep::g_tb_override_active = false; const bool reached = (ppc.pc == end_pc && ppc.Exceptions == 0); + + // Running out of steps is the shadow interpreter giving up, not the module + // diverging. No register has been compared at this point, so there is no + // evidence either way. Reporting it as CTRLFLOW made a default run show + // hundreds of faults that no codegen change could fix, and buried the real + // ones among them. Count it separately and stay quiet. + if (!reached && ppc.Exceptions == 0 && steps >= m_ls_step_cap) + { + ++m_ls_cap_hits; + return; + } + const bool undercharged = reached && (interp_cycles > native_charge + LS_UNDERCHARGE_GRACE) && !end_is_loop_header; std::string diff;