mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 19:45:33 +00:00
tailcall
This commit is contained in:
parent
872a48d616
commit
fc90d84788
@ -75,7 +75,6 @@ static void RemoveBlockFromPageList(Block* block);
|
|||||||
|
|
||||||
static void SetCachedInterpreterHandlers();
|
static void SetCachedInterpreterHandlers();
|
||||||
static void CompileCachedInterpreterBlock(const u32);
|
static void CompileCachedInterpreterBlock(const u32);
|
||||||
static void ExecuteCachedInterpreterBlock(const CachedInterpreterInstruction* cinst);
|
|
||||||
[[noreturn]] static void ExecuteCachedInterpreter();
|
[[noreturn]] static void ExecuteCachedInterpreter();
|
||||||
|
|
||||||
// Fast map provides lookup from PC to function
|
// Fast map provides lookup from PC to function
|
||||||
@ -714,10 +713,78 @@ PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exceptio
|
|||||||
// MARK: - Cached Interpreter
|
// MARK: - Cached Interpreter
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace CPU::CodeCache::CachedInterpreterFunctions {
|
||||||
|
static void CompileOrRevalidateBlock(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void LookupAndExecuteBlock(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void LogCurrentState(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void CheckAndUpdateICacheTags(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void AddDynamicFetchTicks(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void AddUncachedFetchTicks(const CachedInterpreterInstruction* cinst);
|
||||||
|
static void EndBlock(const CachedInterpreterInstruction* cinst);
|
||||||
|
} // namespace CPU::CodeCache::CachedInterpreterFunctions
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::CompileOrRevalidateBlock(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
CompileCachedInterpreterBlock(g_state.pc);
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::LookupAndExecuteBlock(const CachedInterpreterInstruction*)
|
||||||
|
{
|
||||||
|
const u32 pc = g_state.pc;
|
||||||
|
const u32 table = pc >> LUT_TABLE_SHIFT;
|
||||||
|
const u32 idx = (pc & 0xFFFF) >> 2;
|
||||||
|
const CachedInterpreterInstruction* cinst =
|
||||||
|
reinterpret_cast<const CachedInterpreterInstruction*>(g_code_lut[table][idx]);
|
||||||
|
|
||||||
|
#ifdef HAS_MUSTTAIL
|
||||||
|
RETURN_MUSTTAIL(cinst->handler(cinst));
|
||||||
|
#else
|
||||||
|
do
|
||||||
|
{
|
||||||
|
cinst->handler(cinst);
|
||||||
|
cinst++;
|
||||||
|
} while (cinst->handler);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::LogCurrentState(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
CPU::CodeCache::LogCurrentState();
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::CheckAndUpdateICacheTags(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
CPU::CheckAndUpdateICacheTags(cinst->arg);
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::AddDynamicFetchTicks(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
AddPendingTicks(static_cast<TickCount>(
|
||||||
|
cinst->arg *
|
||||||
|
static_cast<u32>(*Bus::GetMemoryAccessTimePtr(g_state.pc & PHYSICAL_MEMORY_ADDRESS_MASK, MemoryAccessSize::Word))));
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::AddUncachedFetchTicks(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
CPU::AddPendingTicks(static_cast<TickCount>(cinst->arg));
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CodeCache::CachedInterpreterFunctions::EndBlock(const CachedInterpreterInstruction* cinst)
|
||||||
|
{
|
||||||
|
// TODO: jump to top of block if looping, block linking, etc.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::CodeCache::SetCachedInterpreterHandlers()
|
void CPU::CodeCache::SetCachedInterpreterHandlers()
|
||||||
{
|
{
|
||||||
static constexpr const CachedInterpreterInstruction compile_or_revalidate_block[] = {
|
static constexpr const CachedInterpreterInstruction compile_or_revalidate_block[] = {
|
||||||
{&CompileCachedInterpreterBlock, 0u},
|
{&CachedInterpreterFunctions::CompileOrRevalidateBlock, 0u},
|
||||||
|
{&CachedInterpreterFunctions::LookupAndExecuteBlock, 0u},
|
||||||
{nullptr, 0u},
|
{nullptr, 0u},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -775,32 +842,26 @@ void CPU::CodeCache::CompileCachedInterpreterBlock(const u32)
|
|||||||
|
|
||||||
if (false)
|
if (false)
|
||||||
{
|
{
|
||||||
cinst->handler = [](u32) { LogCurrentState(); };
|
cinst->handler = &CachedInterpreterFunctions::LogCurrentState;
|
||||||
cinst->arg = 0;
|
cinst->arg = 0;
|
||||||
cinst++;
|
cinst++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block->HasFlag(BlockFlags::IsUsingICache))
|
if (block->HasFlag(BlockFlags::IsUsingICache))
|
||||||
{
|
{
|
||||||
cinst->handler = &CheckAndUpdateICacheTags;
|
cinst->handler = &CachedInterpreterFunctions::CheckAndUpdateICacheTags;
|
||||||
cinst->arg = block->icache_line_count;
|
cinst->arg = block->icache_line_count;
|
||||||
cinst++;
|
cinst++;
|
||||||
}
|
}
|
||||||
else if (block->HasFlag(BlockFlags::NeedsDynamicFetchTicks))
|
else if (block->HasFlag(BlockFlags::NeedsDynamicFetchTicks))
|
||||||
{
|
{
|
||||||
static const auto dynamic_fetch_handler = [](u32 size) {
|
cinst->handler = &CachedInterpreterFunctions::AddDynamicFetchTicks;
|
||||||
AddPendingTicks(
|
|
||||||
static_cast<TickCount>(size * static_cast<u32>(*Bus::GetMemoryAccessTimePtr(
|
|
||||||
g_state.pc & PHYSICAL_MEMORY_ADDRESS_MASK, MemoryAccessSize::Word))));
|
|
||||||
};
|
|
||||||
|
|
||||||
cinst->handler = dynamic_fetch_handler;
|
|
||||||
cinst->arg = block->size;
|
cinst->arg = block->size;
|
||||||
cinst++;
|
cinst++;
|
||||||
}
|
}
|
||||||
else if (block->uncached_fetch_ticks > 0)
|
else if (block->uncached_fetch_ticks > 0)
|
||||||
{
|
{
|
||||||
cinst->handler = reinterpret_cast<CachedInterpreterHandler>(&CPU::AddPendingTicks);
|
cinst->handler = &CachedInterpreterFunctions::AddUncachedFetchTicks;
|
||||||
cinst->arg = static_cast<u32>(block->uncached_fetch_ticks);
|
cinst->arg = static_cast<u32>(block->uncached_fetch_ticks);
|
||||||
cinst++;
|
cinst++;
|
||||||
}
|
}
|
||||||
@ -817,7 +878,11 @@ void CPU::CodeCache::CompileCachedInterpreterBlock(const u32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// end
|
// end
|
||||||
|
#ifdef HAS_MUSTTAIL
|
||||||
|
cinst->handler = &CachedInterpreterFunctions::EndBlock;
|
||||||
|
#else
|
||||||
cinst->handler = nullptr;
|
cinst->handler = nullptr;
|
||||||
|
#endif
|
||||||
cinst->arg = 0;
|
cinst->arg = 0;
|
||||||
cinst++;
|
cinst++;
|
||||||
|
|
||||||
@ -828,21 +893,9 @@ void CPU::CodeCache::CompileCachedInterpreterBlock(const u32)
|
|||||||
CommitCode(required_space);
|
CommitCode(required_space);
|
||||||
MemMap::EndCodeWrite();
|
MemMap::EndCodeWrite();
|
||||||
|
|
||||||
// execute it
|
|
||||||
ExecuteCachedInterpreterBlock(cstart);
|
|
||||||
|
|
||||||
// TODO: Block linking!
|
// TODO: Block linking!
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE_RELEASE void CPU::CodeCache::ExecuteCachedInterpreterBlock(const CachedInterpreterInstruction* cinst)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
cinst->handler(cinst->arg);
|
|
||||||
cinst++;
|
|
||||||
} while (cinst->handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void CPU::CodeCache::ExecuteCachedInterpreter()
|
[[noreturn]] void CPU::CodeCache::ExecuteCachedInterpreter()
|
||||||
{
|
{
|
||||||
#define CHECK_DOWNCOUNT() \
|
#define CHECK_DOWNCOUNT() \
|
||||||
@ -874,7 +927,17 @@ ALWAYS_INLINE_RELEASE void CPU::CodeCache::ExecuteCachedInterpreterBlock(const C
|
|||||||
reexecute_block:
|
reexecute_block:
|
||||||
// Execute block.
|
// Execute block.
|
||||||
DebugAssert(!(HasPendingInterrupt()));
|
DebugAssert(!(HasPendingInterrupt()));
|
||||||
ExecuteCachedInterpreterBlock(cinst);
|
|
||||||
|
#ifdef HAS_MUSTTAIL
|
||||||
|
cinst->handler(cinst);
|
||||||
|
#else
|
||||||
|
do
|
||||||
|
{
|
||||||
|
cinst->handler(cinst);
|
||||||
|
cinst++;
|
||||||
|
} while (cinst->handler);
|
||||||
|
#endif
|
||||||
|
|
||||||
CHECK_DOWNCOUNT();
|
CHECK_DOWNCOUNT();
|
||||||
|
|
||||||
// Handle self-looping blocks
|
// Handle self-looping blocks
|
||||||
|
@ -13,6 +13,11 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#define HAS_MUSTTAIL 1
|
||||||
|
#define RETURN_MUSTTAIL(val) __attribute__((musttail)) return val
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace CPU::CodeCache {
|
namespace CPU::CodeCache {
|
||||||
|
|
||||||
enum : u32
|
enum : u32
|
||||||
@ -205,9 +210,17 @@ struct PageProtectionInfo
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(PageProtectionInfo) == (sizeof(Block*) * 2 + 8));
|
static_assert(sizeof(PageProtectionInfo) == (sizeof(Block*) * 2 + 8));
|
||||||
|
|
||||||
using CachedInterpreterHandler = void(*)(u32 arg);
|
struct CachedInterpreterInstruction;
|
||||||
|
|
||||||
|
using CachedInterpreterHandler = void (*)(const CachedInterpreterInstruction*);
|
||||||
CachedInterpreterHandler GetCachedInterpreterHandler(const Instruction inst);
|
CachedInterpreterHandler GetCachedInterpreterHandler(const Instruction inst);
|
||||||
|
|
||||||
|
#ifdef HAS_MUSTTAIL
|
||||||
|
#define END_CACHED_INTERPRETER_INSTRUCTION(arg) RETURN_MUSTTAIL((arg + 1)->handler(arg + 1));
|
||||||
|
#else
|
||||||
|
#define END_CACHED_INTERPRETER_INSTRUCTION(arg)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct CachedInterpreterInstruction
|
struct CachedInterpreterInstruction
|
||||||
{
|
{
|
||||||
CachedInterpreterHandler handler;
|
CachedInterpreterHandler handler;
|
||||||
|
@ -2775,29 +2775,31 @@ void CPU::SetSingleStepFlag()
|
|||||||
namespace CPU {
|
namespace CPU {
|
||||||
#define MAKE_CACHED_INSTRUCTION_HANDLER(insn) \
|
#define MAKE_CACHED_INSTRUCTION_HANDLER(insn) \
|
||||||
template<PGXPMode pgxp_mode> \
|
template<PGXPMode pgxp_mode> \
|
||||||
static void CachedInstructionHandler_##insn(u32 arg) \
|
static void CachedInstructionHandler_##insn(const CPU::CodeCache::CachedInterpreterInstruction* cinst) \
|
||||||
{ \
|
{ \
|
||||||
|
const Instruction inst{cinst->arg}; \
|
||||||
g_state.pending_ticks++; \
|
g_state.pending_ticks++; \
|
||||||
g_state.current_instruction.bits = arg; \
|
g_state.current_instruction.bits = inst.bits; \
|
||||||
g_state.current_instruction_pc = g_state.pc; \
|
g_state.current_instruction_pc = g_state.pc; \
|
||||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken; \
|
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken; \
|
||||||
g_state.branch_was_taken = false; \
|
g_state.branch_was_taken = false; \
|
||||||
g_state.exception_raised = false; \
|
g_state.exception_raised = false; \
|
||||||
g_state.pc = g_state.npc; \
|
g_state.pc = g_state.npc; \
|
||||||
g_state.npc += 4; \
|
g_state.npc += 4; \
|
||||||
Execute_##insn<pgxp_mode, false>(Instruction{arg}); \
|
Execute_##insn<pgxp_mode, false>(inst); \
|
||||||
UpdateLoadDelay(); /* TODO: For non-load instructions, we don't need to update next_load_delay_reg */ \
|
UpdateLoadDelay(); /* TODO: For non-load instructions, we don't need to update next_load_delay_reg */ \
|
||||||
g_state.next_instruction_is_branch_delay_slot = false; /* FIXME */ \
|
g_state.next_instruction_is_branch_delay_slot = false; /* FIXME */ \
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst); \
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU_FOR_EACH_INSTRUCTION(MAKE_CACHED_INSTRUCTION_HANDLER);
|
CPU_FOR_EACH_INSTRUCTION(MAKE_CACHED_INSTRUCTION_HANDLER);
|
||||||
|
|
||||||
// TODO: inline gte ops
|
// TODO: inline gte ops
|
||||||
|
|
||||||
static void CachedInstructionHandler_gte(u32 arg)
|
static void CachedInstructionHandler_gte(const CPU::CodeCache::CachedInterpreterInstruction* cinst)
|
||||||
{
|
{
|
||||||
g_state.pending_ticks++;
|
g_state.pending_ticks++;
|
||||||
g_state.current_instruction.bits = arg;
|
g_state.current_instruction.bits = cinst->arg;
|
||||||
g_state.current_instruction_pc = g_state.pc;
|
g_state.current_instruction_pc = g_state.pc;
|
||||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
||||||
g_state.branch_was_taken = false;
|
g_state.branch_was_taken = false;
|
||||||
@ -2805,9 +2807,10 @@ static void CachedInstructionHandler_gte(u32 arg)
|
|||||||
g_state.pc = g_state.npc;
|
g_state.pc = g_state.npc;
|
||||||
g_state.npc += 4;
|
g_state.npc += 4;
|
||||||
StallUntilGTEComplete();
|
StallUntilGTEComplete();
|
||||||
GTE::ExecuteInstruction(arg);
|
GTE::ExecuteInstruction(cinst->arg);
|
||||||
UpdateLoadDelay();
|
UpdateLoadDelay();
|
||||||
g_state.next_instruction_is_branch_delay_slot = false; /* FIXME */
|
g_state.next_instruction_is_branch_delay_slot = false; /* FIXME */
|
||||||
|
END_CACHED_INTERPRETER_INSTRUCTION(cinst);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CPU
|
} // namespace CPU
|
||||||
|
Loading…
x
Reference in New Issue
Block a user