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
8 changes: 8 additions & 0 deletions Source/Core/Core/PowerPC/JitCommon/JitBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,14 @@ class JitBase : public CPUCoreBase

virtual const CommonAsmRoutinesBase* GetAsmRoutines() = 0;

// The core that compiled the block a runtime hook fired from, and therefore
// the one whose js sets and block cache a registration belongs in. That is
// normally this core. A core that mostly executes prebuilt code but keeps a
// JIT for whatever it does not cover must name that JIT instead: the JIT is
// what reads those sets at compile time, and the only thing that can retire
// the hook by recompiling the block with the check folded in.
virtual JitBase* GetExceptionCheckTarget() { return this; }

virtual bool WantsPageTableMappings() const;

virtual bool HandleFault(uintptr_t access_address, SContext* ctx) = 0;
Expand Down
17 changes: 12 additions & 5 deletions Source/Core/Core/PowerPC/JitInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,24 @@ void JitInterface::CompileExceptionCheck(ExceptionType type)
if (!m_jit)
return;

// Register against the core that compiled the block this fired from. Under
// static recompilation that is the fallback JIT rather than the static core:
// the fallback is what reads these sets when it compiles, and what retires
// the call site by recompiling with the check folded inline.
JitBase* const target = m_jit->GetExceptionCheckTarget();

std::unordered_set<u32>* exception_addresses = nullptr;

switch (type)
{
case ExceptionType::FIFOWrite:
exception_addresses = &m_jit->js.fifoWriteAddresses;
exception_addresses = &target->js.fifoWriteAddresses;
break;
case ExceptionType::PairedQuantize:
exception_addresses = &m_jit->js.pairedQuantizeAddresses;
exception_addresses = &target->js.pairedQuantizeAddresses;
break;
case ExceptionType::SpeculativeConstants:
exception_addresses = &m_jit->js.noSpeculativeConstantsAddresses;
exception_addresses = &target->js.noSpeculativeConstantsAddresses;
break;
}

Expand All @@ -355,8 +361,9 @@ void JitInterface::CompileExceptionCheck(ExceptionType type)
exception_addresses->insert(ppc_state.pc);

// Invalidate the JIT block so that it gets recompiled with the external exception check
// included.
m_jit->GetBlockCache()->InvalidateICache(ppc_state.pc, 4, true);
// included. Same core as the set above: invalidating a different core's
// cache leaves the block standing and the hook firing.
target->GetBlockCache()->InvalidateICache(ppc_state.pc, 4, true);
}
}

Expand Down
11 changes: 11 additions & 0 deletions Source/Core/Core/PowerPC/StaticRecomp/StaticRecompCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ class StaticRecompCore : public JitBase
bool HandleFault(uintptr_t access_address, SContext* ctx) override { return false; }

JitBaseBlockCache* GetBlockCache() override { return &m_block_cache; }

// Gather-pipe and quantize hooks do not only fire from module code: anything
// the module does not cover runs on m_fallback_jit, a real Jit64/JitArm64.
// That core reads fifoWriteAddresses at compile time and owns the block that
// has to be invalidated, so registering against this core instead left the
// check uncompiled and the hook firing on every gather-pipe store forever.
JitBase* GetExceptionCheckTarget() override
{
return m_fallback_jit ? m_fallback_jit.get() : this;
}

void EraseSingleBlock(const JitBlock& block) override {}
std::vector<MemoryStats> GetMemoryStats() const override { return {}; }
std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const override
Expand Down