CPU/Recompiler: Simplify PC addressing
This commit is contained in:
parent
033d85cd90
commit
6081e2415f
|
@ -21,6 +21,8 @@ bool CodeGenerator::CompileBlock(CodeBlock* block, CodeBlock::HostCodePointer* o
|
||||||
m_block = block;
|
m_block = block;
|
||||||
m_block_start = block->instructions.data();
|
m_block_start = block->instructions.data();
|
||||||
m_block_end = block->instructions.data() + block->instructions.size();
|
m_block_end = block->instructions.data() + block->instructions.size();
|
||||||
|
m_pc = block->GetPC();
|
||||||
|
m_pc_valid = true;
|
||||||
|
|
||||||
EmitBeginBlock();
|
EmitBeginBlock();
|
||||||
BlockPrologue();
|
BlockPrologue();
|
||||||
|
@ -955,7 +957,7 @@ void CodeGenerator::BlockPrologue()
|
||||||
|
|
||||||
EmitStoreCPUStructField(offsetof(State, exception_raised), Value::FromConstantU8(0));
|
EmitStoreCPUStructField(offsetof(State, exception_raised), Value::FromConstantU8(0));
|
||||||
|
|
||||||
if (m_block->uncached_fetch_ticks > 0)
|
if (m_block->uncached_fetch_ticks > 0 || m_block->icache_line_count > 0)
|
||||||
EmitICacheCheckAndUpdate();
|
EmitICacheCheckAndUpdate();
|
||||||
|
|
||||||
// we don't know the state of the last block, so assume load delays might be in progress
|
// we don't know the state of the last block, so assume load delays might be in progress
|
||||||
|
@ -965,10 +967,6 @@ void CodeGenerator::BlockPrologue()
|
||||||
m_current_instruction_was_branch_taken_dirty = false;
|
m_current_instruction_was_branch_taken_dirty = false;
|
||||||
m_load_delay_dirty = true;
|
m_load_delay_dirty = true;
|
||||||
m_gte_busy_cycles_dirty = true;
|
m_gte_busy_cycles_dirty = true;
|
||||||
|
|
||||||
m_pc_offset = 0;
|
|
||||||
m_current_instruction_pc_offset = 0;
|
|
||||||
m_next_pc_offset = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGenerator::BlockEpilogue()
|
void CodeGenerator::BlockEpilogue()
|
||||||
|
@ -992,9 +990,8 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// move instruction offsets forward
|
// move instruction offsets forward
|
||||||
m_current_instruction_pc_offset = m_pc_offset;
|
if (m_pc_valid)
|
||||||
m_pc_offset = m_next_pc_offset;
|
m_pc += 4;
|
||||||
m_next_pc_offset += 4;
|
|
||||||
|
|
||||||
// reset dirty flags
|
// reset dirty flags
|
||||||
if (m_branch_was_taken_dirty)
|
if (m_branch_was_taken_dirty)
|
||||||
|
@ -1129,38 +1126,15 @@ void CodeGenerator::StallUntilGTEComplete()
|
||||||
|
|
||||||
Value CodeGenerator::CalculatePC(u32 offset /* = 0 */)
|
Value CodeGenerator::CalculatePC(u32 offset /* = 0 */)
|
||||||
{
|
{
|
||||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
if (!m_pc_valid)
|
||||||
EmitLoadGuestRegister(value.GetHostRegister(), Reg::pc);
|
Panic("Attempt to get an indeterminate PC");
|
||||||
|
|
||||||
const u32 apply_offset = m_pc_offset + offset;
|
return Value::FromConstantU32(m_pc + offset);
|
||||||
if (apply_offset > 0)
|
|
||||||
EmitAdd(value.GetHostRegister(), value.GetHostRegister(), Value::FromConstantU32(apply_offset), false);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value CodeGenerator::GetCurrentInstructionPC(u32 offset /* = 0 */)
|
Value CodeGenerator::GetCurrentInstructionPC(u32 offset /* = 0 */)
|
||||||
{
|
{
|
||||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
return Value::FromConstantU32(m_current_instruction->pc);
|
||||||
EmitLoadCPUStructField(value.GetHostRegister(), RegSize_32, offsetof(State, current_instruction_pc));
|
|
||||||
|
|
||||||
const u32 apply_offset = m_current_instruction_pc_offset + offset;
|
|
||||||
if (apply_offset > 0)
|
|
||||||
EmitAdd(value.GetHostRegister(), value.GetHostRegister(), Value::FromConstantU32(apply_offset), false);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeGenerator::UpdateCurrentInstructionPC(bool commit)
|
|
||||||
{
|
|
||||||
if (m_current_instruction_pc_offset > 0)
|
|
||||||
{
|
|
||||||
EmitAddCPUStructField(offsetof(State, current_instruction_pc),
|
|
||||||
Value::FromConstantU32(m_current_instruction_pc_offset));
|
|
||||||
|
|
||||||
if (commit)
|
|
||||||
m_current_instruction_pc_offset = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGenerator::WriteNewPC(const Value& value, bool commit)
|
void CodeGenerator::WriteNewPC(const Value& value, bool commit)
|
||||||
|
@ -1168,7 +1142,11 @@ void CodeGenerator::WriteNewPC(const Value& value, bool commit)
|
||||||
// TODO: This _could_ be moved into the register cache, but would it gain anything?
|
// TODO: This _could_ be moved into the register cache, but would it gain anything?
|
||||||
EmitStoreGuestRegister(Reg::pc, value);
|
EmitStoreGuestRegister(Reg::pc, value);
|
||||||
if (commit)
|
if (commit)
|
||||||
m_next_pc_offset = 0;
|
{
|
||||||
|
m_pc_valid = value.IsConstant();
|
||||||
|
if (m_pc_valid)
|
||||||
|
m_pc = static_cast<u32>(value.constant_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
||||||
|
@ -2209,9 +2187,13 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
||||||
m_register_cache.FlushGuestRegister(lr_reg, false, true);
|
m_register_cache.FlushGuestRegister(lr_reg, false, true);
|
||||||
|
|
||||||
// compute return address, which is also set as the new pc when the branch isn't taken
|
// compute return address, which is also set as the new pc when the branch isn't taken
|
||||||
Value next_pc;
|
Value next_pc = CalculatePC(4);
|
||||||
if (condition != Condition::Always || lr_reg != Reg::count)
|
DebugAssert(next_pc.IsConstant());
|
||||||
next_pc = CalculatePC(4);
|
if (condition != Condition::Always)
|
||||||
|
{
|
||||||
|
next_pc = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
EmitCopyValue(next_pc.GetHostRegister(), CalculatePC(4));
|
||||||
|
}
|
||||||
|
|
||||||
LabelType branch_not_taken;
|
LabelType branch_not_taken;
|
||||||
if (condition != Condition::Always)
|
if (condition != Condition::Always)
|
||||||
|
|
|
@ -206,7 +206,6 @@ private:
|
||||||
|
|
||||||
Value CalculatePC(u32 offset = 0);
|
Value CalculatePC(u32 offset = 0);
|
||||||
Value GetCurrentInstructionPC(u32 offset = 0);
|
Value GetCurrentInstructionPC(u32 offset = 0);
|
||||||
void UpdateCurrentInstructionPC(bool commit);
|
|
||||||
void WriteNewPC(const Value& value, bool commit);
|
void WriteNewPC(const Value& value, bool commit);
|
||||||
|
|
||||||
Value DoGTERegisterRead(u32 index);
|
Value DoGTERegisterRead(u32 index);
|
||||||
|
@ -248,9 +247,9 @@ private:
|
||||||
|
|
||||||
TickCount m_delayed_cycles_add = 0;
|
TickCount m_delayed_cycles_add = 0;
|
||||||
TickCount m_gte_done_cycle = 0;
|
TickCount m_gte_done_cycle = 0;
|
||||||
TickCount m_pc_offset = 0;
|
|
||||||
TickCount m_current_instruction_pc_offset = 0;
|
u32 m_pc = 0;
|
||||||
TickCount m_next_pc_offset = 0;
|
bool m_pc_valid = false;
|
||||||
|
|
||||||
// whether various flags need to be reset.
|
// whether various flags need to be reset.
|
||||||
bool m_current_instruction_in_branch_delay_slot_dirty = false;
|
bool m_current_instruction_in_branch_delay_slot_dirty = false;
|
||||||
|
|
|
@ -2250,7 +2250,8 @@ CodeCache::DispatcherFunction CodeGenerator::CompileDispatcher()
|
||||||
m_emit->str(a64::w8, a64::MemOperand(GetHostReg64(RCPUPTR), offsetof(State, current_instruction_pc)));
|
m_emit->str(a64::w8, a64::MemOperand(GetHostReg64(RCPUPTR), offsetof(State, current_instruction_pc)));
|
||||||
|
|
||||||
// blr(x9[pc * 2]) (fast_map[pc >> 2])
|
// blr(x9[pc * 2]) (fast_map[pc >> 2])
|
||||||
m_emit->ldr(a64::x8, a64::MemOperand(a64::x9, a64::x8, a64::LSL, 2));
|
m_emit->lsr(a64::w8, a64::w8, 2);
|
||||||
|
m_emit->ldr(a64::x8, a64::MemOperand(a64::x9, a64::x8, a64::LSL, 3));
|
||||||
m_emit->blr(a64::x8);
|
m_emit->blr(a64::x8);
|
||||||
|
|
||||||
// end while
|
// end while
|
||||||
|
|
|
@ -164,45 +164,49 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
|
||||||
|
|
||||||
void CodeGenerator::EmitICacheCheckAndUpdate()
|
void CodeGenerator::EmitICacheCheckAndUpdate()
|
||||||
{
|
{
|
||||||
Value pc = CalculatePC();
|
|
||||||
Value temp = m_register_cache.AllocateScratch(RegSize_32);
|
Value temp = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
m_register_cache.InhibitAllocation();
|
|
||||||
|
|
||||||
EmitShr(temp.GetHostRegister(), pc.GetHostRegister(), RegSize_32, Value::FromConstantU32(29));
|
if (GetSegmentForAddress(m_pc) >= Segment::KSEG1)
|
||||||
LabelType is_cached;
|
|
||||||
LabelType ready_to_execute;
|
|
||||||
EmitConditionalBranch(Condition::LessEqual, false, temp.GetHostRegister(), Value::FromConstantU32(4), &is_cached);
|
|
||||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, pending_ticks));
|
|
||||||
EmitAdd(temp.host_reg, temp.host_reg, Value::FromConstantU32(static_cast<u32>(m_block->uncached_fetch_ticks)), false);
|
|
||||||
EmitStoreCPUStructField(offsetof(State, pending_ticks), temp);
|
|
||||||
EmitBranch(&ready_to_execute);
|
|
||||||
EmitBindLabel(&is_cached);
|
|
||||||
|
|
||||||
// cached path
|
|
||||||
EmitAnd(pc.GetHostRegister(), pc.GetHostRegister(), Value::FromConstantU32(ICACHE_TAG_ADDRESS_MASK));
|
|
||||||
VirtualMemoryAddress current_address = (m_block->instructions[0].pc & ICACHE_TAG_ADDRESS_MASK);
|
|
||||||
for (u32 i = 0; i < m_block->icache_line_count; i++, current_address += ICACHE_LINE_SIZE)
|
|
||||||
{
|
{
|
||||||
const TickCount fill_ticks = GetICacheFillTicks(current_address);
|
EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, offsetof(State, pending_ticks));
|
||||||
if (fill_ticks <= 0)
|
EmitAdd(temp.GetHostRegister(), temp.GetHostRegister(),
|
||||||
continue;
|
Value::FromConstantU32(static_cast<u32>(m_block->uncached_fetch_ticks)), false);
|
||||||
|
|
||||||
const u32 line = GetICacheLine(current_address);
|
|
||||||
const u32 offset = offsetof(State, icache_tags) + (line * sizeof(u32));
|
|
||||||
LabelType cache_hit;
|
|
||||||
|
|
||||||
EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, offset);
|
|
||||||
EmitConditionalBranch(Condition::Equal, false, temp.GetHostRegister(), pc, &cache_hit);
|
|
||||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, pending_ticks));
|
|
||||||
EmitStoreCPUStructField(offset, pc);
|
|
||||||
EmitAdd(temp.host_reg, temp.host_reg, Value::FromConstantU32(static_cast<u32>(fill_ticks)), false);
|
|
||||||
EmitStoreCPUStructField(offsetof(State, pending_ticks), temp);
|
EmitStoreCPUStructField(offsetof(State, pending_ticks), temp);
|
||||||
EmitBindLabel(&cache_hit);
|
|
||||||
EmitAdd(pc.GetHostRegister(), pc.GetHostRegister(), Value::FromConstantU32(ICACHE_LINE_SIZE), false);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// cached path
|
||||||
|
Value temp2 = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
|
||||||
EmitBindLabel(&ready_to_execute);
|
m_register_cache.InhibitAllocation();
|
||||||
m_register_cache.UninhibitAllocation();
|
|
||||||
|
VirtualMemoryAddress current_pc = m_pc & ICACHE_TAG_ADDRESS_MASK;
|
||||||
|
for (u32 i = 0; i < m_block->icache_line_count; i++, current_pc += ICACHE_LINE_SIZE)
|
||||||
|
{
|
||||||
|
const VirtualMemoryAddress tag = GetICacheTagForAddress(current_pc);
|
||||||
|
const TickCount fill_ticks = GetICacheFillTicks(current_pc);
|
||||||
|
if (fill_ticks <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u32 line = GetICacheLine(current_pc);
|
||||||
|
const u32 offset = offsetof(State, icache_tags) + (line * sizeof(u32));
|
||||||
|
LabelType cache_hit;
|
||||||
|
|
||||||
|
EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, offset);
|
||||||
|
EmitCopyValue(temp2.GetHostRegister(), Value::FromConstantU32(current_pc));
|
||||||
|
EmitCmp(temp2.GetHostRegister(), temp);
|
||||||
|
EmitConditionalBranch(Condition::Equal, false, temp.GetHostRegister(), temp2, &cache_hit);
|
||||||
|
|
||||||
|
EmitLoadCPUStructField(temp.GetHostRegister(), RegSize_32, offsetof(State, pending_ticks));
|
||||||
|
EmitStoreCPUStructField(offset, temp2);
|
||||||
|
EmitAdd(temp.GetHostRegister(), temp.GetHostRegister(), Value::FromConstantU32(static_cast<u32>(fill_ticks)),
|
||||||
|
false);
|
||||||
|
EmitStoreCPUStructField(offsetof(State, pending_ticks), temp);
|
||||||
|
EmitBindLabel(&cache_hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_register_cache.UninhibitAllocation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2612,48 +2612,32 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
|
||||||
|
|
||||||
void CodeGenerator::EmitICacheCheckAndUpdate()
|
void CodeGenerator::EmitICacheCheckAndUpdate()
|
||||||
{
|
{
|
||||||
Value pc = CalculatePC();
|
if (GetSegmentForAddress(m_pc) >= Segment::KSEG1)
|
||||||
Value seg = m_register_cache.AllocateScratch(RegSize_32);
|
|
||||||
m_register_cache.InhibitAllocation();
|
|
||||||
|
|
||||||
m_emit->mov(GetHostReg32(seg), GetHostReg32(pc));
|
|
||||||
m_emit->shr(GetHostReg32(seg), 29);
|
|
||||||
|
|
||||||
Xbyak::Label is_cached;
|
|
||||||
m_emit->cmp(GetHostReg32(seg), 4);
|
|
||||||
m_emit->jle(is_cached);
|
|
||||||
|
|
||||||
// uncached
|
|
||||||
Xbyak::Label done;
|
|
||||||
m_emit->add(m_emit->dword[GetCPUPtrReg() + offsetof(State, pending_ticks)],
|
|
||||||
static_cast<u32>(m_block->uncached_fetch_ticks));
|
|
||||||
m_emit->jmp(done, Xbyak::CodeGenerator::T_NEAR);
|
|
||||||
|
|
||||||
// cached
|
|
||||||
m_emit->L(is_cached);
|
|
||||||
m_emit->and_(GetHostReg32(pc), ICACHE_TAG_ADDRESS_MASK);
|
|
||||||
|
|
||||||
VirtualMemoryAddress current_address = (m_block->instructions[0].pc & ICACHE_TAG_ADDRESS_MASK);
|
|
||||||
for (u32 i = 0; i < m_block->icache_line_count; i++, current_address += ICACHE_LINE_SIZE)
|
|
||||||
{
|
{
|
||||||
const TickCount fill_ticks = GetICacheFillTicks(current_address);
|
m_emit->add(m_emit->dword[GetCPUPtrReg() + offsetof(State, pending_ticks)],
|
||||||
if (fill_ticks <= 0)
|
static_cast<u32>(m_block->uncached_fetch_ticks));
|
||||||
continue;
|
|
||||||
|
|
||||||
const u32 line = GetICacheLine(current_address);
|
|
||||||
const u32 offset = offsetof(State, icache_tags) + (line * sizeof(u32));
|
|
||||||
Xbyak::Label cache_hit;
|
|
||||||
|
|
||||||
m_emit->cmp(GetHostReg32(pc), m_emit->dword[GetCPUPtrReg() + offset]);
|
|
||||||
m_emit->je(cache_hit);
|
|
||||||
m_emit->mov(m_emit->dword[GetCPUPtrReg() + offset], GetHostReg32(pc));
|
|
||||||
m_emit->add(m_emit->dword[GetCPUPtrReg() + offsetof(State, pending_ticks)], static_cast<u32>(fill_ticks));
|
|
||||||
m_emit->L(cache_hit);
|
|
||||||
m_emit->add(GetHostReg32(pc), ICACHE_LINE_SIZE);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VirtualMemoryAddress current_pc = m_pc & ICACHE_TAG_ADDRESS_MASK;
|
||||||
|
for (u32 i = 0; i < m_block->icache_line_count; i++, current_pc += ICACHE_LINE_SIZE)
|
||||||
|
{
|
||||||
|
const VirtualMemoryAddress tag = GetICacheTagForAddress(current_pc);
|
||||||
|
const TickCount fill_ticks = GetICacheFillTicks(current_pc);
|
||||||
|
if (fill_ticks <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
m_emit->L(done);
|
const u32 line = GetICacheLine(current_pc);
|
||||||
m_register_cache.UninhibitAllocation();
|
const u32 offset = offsetof(State, icache_tags) + (line * sizeof(u32));
|
||||||
|
Xbyak::Label cache_hit;
|
||||||
|
|
||||||
|
m_emit->cmp(m_emit->dword[GetCPUPtrReg() + offset], tag);
|
||||||
|
m_emit->je(cache_hit);
|
||||||
|
m_emit->mov(m_emit->dword[GetCPUPtrReg() + offset], tag);
|
||||||
|
m_emit->add(m_emit->dword[GetCPUPtrReg() + offsetof(State, pending_ticks)], static_cast<u32>(fill_ticks));
|
||||||
|
m_emit->L(cache_hit);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGenerator::EmitStallUntilGTEComplete()
|
void CodeGenerator::EmitStallUntilGTEComplete()
|
||||||
|
|
Loading…
Reference in New Issue