This commit is contained in:
Connor McLaughlin 2020-10-11 01:13:16 +10:00
parent d950779662
commit d15d1e817a
7 changed files with 144 additions and 0 deletions

View File

@ -168,6 +168,8 @@ static void ExecuteImpl()
CheckAndUpdateICacheTags(block->icache_line_count, block->uncached_fetch_ticks); CheckAndUpdateICacheTags(block->icache_line_count, block->uncached_fetch_ticks);
InterpretCachedBlock<pgxp_mode>(*block); InterpretCachedBlock<pgxp_mode>(*block);
if (block->is_idle_loop)
IdleSkip();
if (g_state.pending_ticks >= g_state.downcount) if (g_state.pending_ticks >= g_state.downcount)
break; break;
@ -387,6 +389,68 @@ recompile:
return true; return true;
} }
static bool IsIdleLoop(u32 pc)
{
enum InstructionType
{
Match,
FollowBranch,
BranchThreshold
};
struct InstructionAndMask
{
InstructionType type;
u32 instruction;
u32 mask;
};
static constexpr InstructionAndMask idle_1[] = {
{InstructionType::Match, 0x8fa20000, 0xFFFF0000}, // lw v0, 16(sp)
{InstructionType::Match, 0x00000000, 0xFFFFFFFF}, // sll $zero, $zero, 0
{InstructionType::Match, 0x2442ffff, 0xFFFFFFFF}, // addiu v0, v0, ffff
{InstructionType::Match, 0xafa20000, 0xFFFF0000}, // sw v0, 16(sp)
{InstructionType::Match, 0x8fa20000, 0xFFFF0000}, // lw v0, 16(sp)
{InstructionType::Match, 0x00000000, 0xFFFFFFFF}, // sll $zero, $zero, 0
{InstructionType::FollowBranch, 0x14430000, 0xFFFF0000}, // bne v0, v1, 8003228c
{InstructionType::Match, 0x3c020000, 0xFFFF0000}, // lui v0, 8007
{InstructionType::Match, 0x8c420000, 0xFFFF0000}, // lw v0, -23808(v0)
{InstructionType::Match, 0x00000000, 0xFFFFFFFF}, // sll $zero, $zero, 0
{InstructionType::Match, 0x0044102a, 0xFFFFFFFF}, // slt v0, v0, a0
{InstructionType::BranchThreshold, 0x14400000, 0xFFFF0000}, // bne v0, $zero, 80032244
};
u32 current_pc = pc;
bool match = true;
for (const InstructionAndMask& im : idle_1)
{
Instruction instruction;
if (!SafeReadInstruction(current_pc, &instruction.bits) || (instruction.bits & im.mask) != im.instruction)
{
match = false;
break;
}
if (im.type == FollowBranch)
{
if (!CPU::IsFollowableBranchInstruction(instruction, current_pc, pc, 128))
break;
current_pc = CPU::GetBranchInstructionTarget(instruction, current_pc);
continue;
}
if (im.type == BranchThreshold)
{
if (!CPU::IsFollowableBranchInstruction(instruction, current_pc, pc, 128))
break;
}
current_pc += sizeof(u32);
}
return match;
}
bool CompileBlock(CodeBlock* block) bool CompileBlock(CodeBlock* block)
{ {
u32 pc = block->GetPC(); u32 pc = block->GetPC();
@ -398,6 +462,10 @@ bool CompileBlock(CodeBlock* block)
__debugbreak(); __debugbreak();
#endif #endif
block->is_idle_loop = IsIdleLoop(pc);
if (block->is_idle_loop)
Log_InfoPrintf("Idle loop found at 0x%08X", pc);
u32 last_cache_line = ICACHE_LINES; u32 last_cache_line = ICACHE_LINES;
for (;;) for (;;)

View File

@ -64,6 +64,7 @@ struct CodeBlock
TickCount uncached_fetch_ticks = 0; TickCount uncached_fetch_ticks = 0;
u32 icache_line_count = 0; u32 icache_line_count = 0;
bool invalidated = false; bool invalidated = false;
bool is_idle_loop = false;
const u32 GetPC() const { return key.GetPC(); } const u32 GetPC() const { return key.GetPC(); }
const u32 GetSizeInBytes() const { return static_cast<u32>(instructions.size()) * sizeof(Instruction); } const u32 GetSizeInBytes() const { return static_cast<u32>(instructions.size()) * sizeof(Instruction); }

View File

@ -237,6 +237,16 @@ void ClearExternalInterrupt(u8 bit)
g_state.cop0_regs.cause.Ip &= static_cast<u8>(~(1u << bit)); g_state.cop0_regs.cause.Ip &= static_cast<u8>(~(1u << bit));
} }
void IdleSkip()
{
const TickCount ticks_to_skip = g_state.downcount - g_state.pending_ticks;
if (ticks_to_skip <= 0)
return;
Log_DebugPrintf("Skipping %d ticks", ticks_to_skip);
g_state.pending_ticks += ticks_to_skip;
}
ALWAYS_INLINE_RELEASE static void UpdateLoadDelay() ALWAYS_INLINE_RELEASE static void UpdateLoadDelay()
{ {
// the old value is needed in case the delay slot instruction overwrites the same register // the old value is needed in case the delay slot instruction overwrites the same register

View File

@ -6,6 +6,7 @@ namespace CPU {
// exceptions // exceptions
void RaiseException(Exception excode); void RaiseException(Exception excode);
void RaiseException(u32 CAUSE_bits, u32 EPC); void RaiseException(u32 CAUSE_bits, u32 EPC);
void IdleSkip();
ALWAYS_INLINE static bool HasPendingInterrupt() ALWAYS_INLINE static bool HasPendingInterrupt()
{ {

View File

@ -866,6 +866,9 @@ void CodeGenerator::BlockEpilogue()
m_register_cache.WriteLoadDelayToCPU(true); m_register_cache.WriteLoadDelayToCPU(true);
AddPendingCycles(true); AddPendingCycles(true);
if (m_block->is_idle_loop)
EmitFunctionCall(nullptr, &CPU::IdleSkip);
} }
void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCount cycles, void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCount cycles,

View File

@ -44,6 +44,64 @@ bool IsBranchInstruction(const Instruction& instruction)
} }
} }
bool IsFollowableBranchInstruction(const Instruction& instruction, u32 instruction_pc, u32 block_pc,
u32 threshold_in_instructions)
{
switch (instruction.op)
{
case InstructionOp::b:
{
// skip when linking
if ((static_cast<u8>(instruction.i.rt.GetValue()) & u8(0x1E)) == u8(0x10))
return false;
if (static_cast<u32>(std::abs(static_cast<s32>(instruction.i.imm_sext32()))) > threshold_in_instructions)
return false;
}
break;
case InstructionOp::beq:
case InstructionOp::bgtz:
case InstructionOp::blez:
case InstructionOp::bne:
{
if (static_cast<u32>(std::abs(static_cast<s32>(instruction.i.imm_sext32()))) > threshold_in_instructions)
return false;
}
break;
default:
return false;
}
// we can't branch before the start of the block...
const u32 branch_target = GetBranchInstructionTarget(instruction, instruction_pc);
if (branch_target < block_pc)
return false;
return true;
}
u32 GetBranchInstructionTarget(const Instruction& instruction, u32 instruction_pc)
{
switch (instruction.op)
{
case InstructionOp::j:
case InstructionOp::jal:
return ((instruction_pc + 4) & UINT32_C(0xF0000000)) | (instruction.j.target << 2);
case InstructionOp::b:
case InstructionOp::beq:
case InstructionOp::bgtz:
case InstructionOp::blez:
case InstructionOp::bne:
return instruction_pc + 4 + (instruction.i.imm_sext32() << 2);
default:
return instruction_pc;
}
}
bool IsMemoryLoadInstruction(const Instruction& instruction) bool IsMemoryLoadInstruction(const Instruction& instruction)
{ {
switch (instruction.op) switch (instruction.op)

View File

@ -218,6 +218,9 @@ bool InstructionHasLoadDelay(const Instruction& instruction);
bool IsExitBlockInstruction(const Instruction& instruction); bool IsExitBlockInstruction(const Instruction& instruction);
bool CanInstructionTrap(const Instruction& instruction, bool in_user_mode); bool CanInstructionTrap(const Instruction& instruction, bool in_user_mode);
bool IsInvalidInstruction(const Instruction& instruction); bool IsInvalidInstruction(const Instruction& instruction);
bool IsFollowableBranchInstruction(const Instruction& instruction, u32 instruction_pc, u32 block_pc,
u32 threshold_in_instructions);
u32 GetBranchInstructionTarget(const Instruction& instruction, u32 instruction_pc);
struct Registers struct Registers
{ {