mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-23 05:49:43 +00:00
wip
This commit is contained in:
parent
d950779662
commit
d15d1e817a
@ -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 (;;)
|
||||||
|
@ -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); }
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user