CPU/Recompiler: Implement PGXP CPU mode

This commit is contained in:
Connor McLaughlin 2021-02-18 00:40:06 +10:00
parent 0bfa1bf873
commit bc021ddfd9
3 changed files with 152 additions and 23 deletions

View file

@ -1140,6 +1140,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = OrValues(lhs, rhs); result = OrValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs | *spec_rhs; spec_value = *spec_lhs | *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_ORI, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
} }
break; break;
@ -1148,6 +1151,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = AndValues(lhs, rhs); result = AndValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs & *spec_rhs; spec_value = *spec_lhs & *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_ANDI, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
} }
break; break;
@ -1156,6 +1162,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = XorValues(lhs, rhs); result = XorValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs ^ *spec_rhs; spec_value = *spec_lhs ^ *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_XORI, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
} }
break; break;
@ -1168,6 +1177,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = OrValues(lhs, rhs); result = OrValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs | *spec_rhs; spec_value = *spec_lhs | *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_OR_, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
} }
break; break;
@ -1176,6 +1188,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = AndValues(lhs, rhs); result = AndValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs & *spec_rhs; spec_value = *spec_lhs & *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_AND_, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
} }
break; break;
@ -1184,6 +1199,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = XorValues(lhs, rhs); result = XorValues(lhs, rhs);
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = *spec_lhs ^ *spec_rhs; spec_value = *spec_lhs ^ *spec_rhs;
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_XOR_, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
} }
break; break;
@ -1192,6 +1210,9 @@ bool CodeGenerator::Compile_Bitwise(const CodeBlockInstruction& cbi)
result = NotValue(OrValues(lhs, rhs)); result = NotValue(OrValues(lhs, rhs));
if (spec_lhs && spec_rhs) if (spec_lhs && spec_rhs)
spec_value = ~(*spec_lhs | *spec_rhs); spec_value = ~(*spec_lhs | *spec_rhs);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_NOR, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
} }
break; break;
@ -1246,6 +1267,14 @@ bool CodeGenerator::Compile_Shift(const CodeBlockInstruction& cbi)
result = ShlValues(rt, shamt, false); result = ShlValues(rt, shamt, false);
if (rt_spec && shamt_spec) if (rt_spec && shamt_spec)
result_spec = *rt_spec << *shamt_spec; result_spec = *rt_spec << *shamt_spec;
if (g_settings.UsingPGXPCPUMode())
{
if (cbi.instruction.r.funct == InstructionFunct::sll)
EmitFunctionCall(nullptr, &PGXP::CPU_SLL, Value::FromConstantU32(cbi.instruction.bits), result, rt);
else // if (cbi.instruction.r.funct == InstructionFunct::sllv)
EmitFunctionCall(nullptr, &PGXP::CPU_SLLV, Value::FromConstantU32(cbi.instruction.bits), result, rt, shamt);
}
} }
break; break;
@ -1255,6 +1284,14 @@ bool CodeGenerator::Compile_Shift(const CodeBlockInstruction& cbi)
result = ShrValues(rt, shamt, false); result = ShrValues(rt, shamt, false);
if (rt_spec && shamt_spec) if (rt_spec && shamt_spec)
result_spec = *rt_spec >> *shamt_spec; result_spec = *rt_spec >> *shamt_spec;
if (g_settings.UsingPGXPCPUMode())
{
if (cbi.instruction.r.funct == InstructionFunct::srl)
EmitFunctionCall(nullptr, &PGXP::CPU_SRL, Value::FromConstantU32(cbi.instruction.bits), result, rt);
else // if (cbi.instruction.r.funct == InstructionFunct::srlv)
EmitFunctionCall(nullptr, &PGXP::CPU_SRLV, Value::FromConstantU32(cbi.instruction.bits), result, rt, shamt);
}
} }
break; break;
@ -1264,6 +1301,14 @@ bool CodeGenerator::Compile_Shift(const CodeBlockInstruction& cbi)
result = SarValues(rt, shamt, false); result = SarValues(rt, shamt, false);
if (rt_spec && shamt_spec) if (rt_spec && shamt_spec)
result_spec = static_cast<u32>(static_cast<s32>(*rt_spec) << *shamt_spec); result_spec = static_cast<u32>(static_cast<s32>(*rt_spec) << *shamt_spec);
if (g_settings.UsingPGXPCPUMode())
{
if (cbi.instruction.r.funct == InstructionFunct::sra)
EmitFunctionCall(nullptr, &PGXP::CPU_SRA, Value::FromConstantU32(cbi.instruction.bits), result, rt);
else // if (cbi.instruction.r.funct == InstructionFunct::srav)
EmitFunctionCall(nullptr, &PGXP::CPU_SRAV, Value::FromConstantU32(cbi.instruction.bits), result, rt, shamt);
}
} }
break; break;
@ -1579,22 +1624,46 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
switch (cbi.instruction.r.funct) switch (cbi.instruction.r.funct)
{ {
case InstructionFunct::mfhi: case InstructionFunct::mfhi:
m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, m_register_cache.ReadGuestRegister(Reg::hi)); {
Value hi = m_register_cache.ReadGuestRegister(Reg::hi);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MFHI, Value::FromConstantU32(cbi.instruction.bits), hi);
m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, std::move(hi));
SpeculativeWriteReg(cbi.instruction.r.rd, std::nullopt); SpeculativeWriteReg(cbi.instruction.r.rd, std::nullopt);
break; }
break;
case InstructionFunct::mthi: case InstructionFunct::mthi:
m_register_cache.WriteGuestRegister(Reg::hi, m_register_cache.ReadGuestRegister(cbi.instruction.r.rs)); {
break; Value rs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MTHI, Value::FromConstantU32(cbi.instruction.bits), rs);
m_register_cache.WriteGuestRegister(Reg::hi, std::move(rs));
}
break;
case InstructionFunct::mflo: case InstructionFunct::mflo:
m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, m_register_cache.ReadGuestRegister(Reg::lo)); {
Value lo = m_register_cache.ReadGuestRegister(Reg::lo);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MFLO, Value::FromConstantU32(cbi.instruction.bits), lo);
m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, std::move(lo));
SpeculativeWriteReg(cbi.instruction.r.rd, std::nullopt); SpeculativeWriteReg(cbi.instruction.r.rd, std::nullopt);
break; }
break;
case InstructionFunct::mtlo: case InstructionFunct::mtlo:
m_register_cache.WriteGuestRegister(Reg::lo, m_register_cache.ReadGuestRegister(cbi.instruction.r.rs)); {
break; Value rs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MTLO, Value::FromConstantU32(cbi.instruction.bits), rs);
m_register_cache.WriteGuestRegister(Reg::lo, std::move(rs));
}
break;
default: default:
UnreachableCode(); UnreachableCode();
@ -1652,16 +1721,32 @@ bool CodeGenerator::Compile_Add(const CodeBlockInstruction& cbi)
} }
// detect register moves and handle them for pgxp // detect register moves and handle them for pgxp
bool do_pgxp_cpu = g_settings.UsingPGXPCPUMode();
if (g_settings.gpu_pgxp_enable && rhs.HasConstantValue(0)) if (g_settings.gpu_pgxp_enable && rhs.HasConstantValue(0))
{ {
EmitFunctionCall(nullptr, &PGXP::CPU_MOVE, EmitFunctionCall(nullptr, &PGXP::CPU_MOVE,
Value::FromConstantU32((static_cast<u32>(dest) << 8) | (static_cast<u32>(lhs_src))), lhs); Value::FromConstantU32((static_cast<u32>(dest) << 8) | (static_cast<u32>(lhs_src))), lhs);
do_pgxp_cpu = false;
} }
Value result = AddValues(lhs, rhs, check_overflow); Value result = AddValues(lhs, rhs, check_overflow);
if (check_overflow) if (check_overflow)
GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow); GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow);
if (do_pgxp_cpu)
{
if (cbi.instruction.op != InstructionOp::funct)
{
// addiu/addiu
EmitFunctionCall(nullptr, &PGXP::CPU_ADDI, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
}
else
{
// add/addu
EmitFunctionCall(nullptr, &PGXP::CPU_ADD, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
}
}
m_register_cache.WriteGuestRegister(dest, std::move(result)); m_register_cache.WriteGuestRegister(dest, std::move(result));
SpeculativeValue value_spec; SpeculativeValue value_spec;
@ -1690,6 +1775,9 @@ bool CodeGenerator::Compile_Subtract(const CodeBlockInstruction& cbi)
if (check_overflow) if (check_overflow)
GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow); GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_SUB, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, std::move(result)); m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, std::move(result));
SpeculativeValue value_spec; SpeculativeValue value_spec;
@ -1706,8 +1794,14 @@ bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi)
InstructionPrologue(cbi, 1); InstructionPrologue(cbi, 1);
const bool signed_multiply = (cbi.instruction.r.funct == InstructionFunct::mult); const bool signed_multiply = (cbi.instruction.r.funct == InstructionFunct::mult);
std::pair<Value, Value> result = MulValues(m_register_cache.ReadGuestRegister(cbi.instruction.r.rs), Value rs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
m_register_cache.ReadGuestRegister(cbi.instruction.r.rt), signed_multiply); Value rt = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, signed_multiply ? &PGXP::CPU_MULT : &PGXP::CPU_MULTU, rs, rt);
std::pair<Value, Value> result = MulValues(rs, rt, signed_multiply);
rs.ReleaseAndClear();
rt.ReleaseAndClear();
m_register_cache.WriteGuestRegister(Reg::hi, std::move(result.first)); m_register_cache.WriteGuestRegister(Reg::hi, std::move(result.first));
m_register_cache.WriteGuestRegister(Reg::lo, std::move(result.second)); m_register_cache.WriteGuestRegister(Reg::lo, std::move(result.second));
@ -1764,6 +1858,10 @@ bool CodeGenerator::Compile_Divide(const CodeBlockInstruction& cbi)
Value num = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs); Value num = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
Value denom = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt); Value denom = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_DIV, num, denom);
if (num.IsConstant() && denom.IsConstant()) if (num.IsConstant() && denom.IsConstant())
{ {
const auto [lo, hi] = MIPSDivide(static_cast<u32>(num.constant_value), static_cast<u32>(denom.constant_value)); const auto [lo, hi] = MIPSDivide(static_cast<u32>(num.constant_value), static_cast<u32>(denom.constant_value));
@ -1821,6 +1919,10 @@ bool CodeGenerator::Compile_SignedDivide(const CodeBlockInstruction& cbi)
Value num = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs); Value num = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
Value denom = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt); Value denom = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_DIVU, num, denom);
if (num.IsConstant() && denom.IsConstant()) if (num.IsConstant() && denom.IsConstant())
{ {
const auto [lo, hi] = MIPSDivide(num.GetS32ConstantValue(), denom.GetS32ConstantValue()); const auto [lo, hi] = MIPSDivide(num.GetS32ConstantValue(), denom.GetS32ConstantValue());
@ -1938,6 +2040,19 @@ bool CodeGenerator::Compile_SetLess(const CodeBlockInstruction& cbi)
Value result = m_register_cache.AllocateScratch(RegSize_32); Value result = m_register_cache.AllocateScratch(RegSize_32);
EmitCmp(lhs.host_reg, rhs); EmitCmp(lhs.host_reg, rhs);
EmitSetConditionResult(result.host_reg, result.size, signed_comparison ? Condition::Less : Condition::Below); EmitSetConditionResult(result.host_reg, result.size, signed_comparison ? Condition::Less : Condition::Below);
if (g_settings.UsingPGXPCPUMode())
{
if (cbi.instruction.op == InstructionOp::slti)
EmitFunctionCall(nullptr, &PGXP::CPU_SLTI, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
else if (cbi.instruction.op == InstructionOp::sltiu)
EmitFunctionCall(nullptr, &PGXP::CPU_SLTIU, Value::FromConstantU32(cbi.instruction.bits), result, lhs);
else if (cbi.instruction.r.funct == InstructionFunct::slt)
EmitFunctionCall(nullptr, &PGXP::CPU_SLT, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
else // if (cbi.instruction.r.funct == InstructionFunct::sltu)
EmitFunctionCall(nullptr, &PGXP::CPU_SLTU, Value::FromConstantU32(cbi.instruction.bits), result, lhs, rhs);
}
m_register_cache.WriteGuestRegister(dest, std::move(result)); m_register_cache.WriteGuestRegister(dest, std::move(result));
SpeculativeValue value_spec; SpeculativeValue value_spec;
@ -2161,6 +2276,14 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
// rt <- (imm << 16) // rt <- (imm << 16)
const u32 value = cbi.instruction.i.imm_zext32() << 16; const u32 value = cbi.instruction.i.imm_zext32() << 16;
if (g_settings.UsingPGXPCPUMode())
{
// TODO: rtVal can be skipped here, and probably worthwhile given this is a common instruction.
EmitFunctionCall(nullptr, &PGXP::CPU_LUI, Value::FromConstantU32(cbi.instruction.bits),
Value::FromConstantU32(value));
}
m_register_cache.WriteGuestRegister(cbi.instruction.i.rt, Value::FromConstantU32(value)); m_register_cache.WriteGuestRegister(cbi.instruction.i.rt, Value::FromConstantU32(value));
SpeculativeWriteReg(cbi.instruction.i.rt, value); SpeculativeWriteReg(cbi.instruction.i.rt, value);
@ -2245,6 +2368,10 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
// coprocessor loads are load-delayed // coprocessor loads are load-delayed
Value value = m_register_cache.AllocateScratch(RegSize_32); Value value = m_register_cache.AllocateScratch(RegSize_32);
EmitLoadCPUStructField(value.host_reg, value.size, offset); EmitLoadCPUStructField(value.host_reg, value.size, offset);
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MFC0, Value::FromConstantU32(cbi.instruction.bits), value);
m_register_cache.WriteGuestRegisterDelayed(cbi.instruction.r.rt, std::move(value)); m_register_cache.WriteGuestRegisterDelayed(cbi.instruction.r.rt, std::move(value));
SpeculativeWriteReg(cbi.instruction.r.rt, std::nullopt); SpeculativeWriteReg(cbi.instruction.r.rt, std::nullopt);
} }
@ -2257,7 +2384,18 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
if (write_mask != UINT32_C(0xFFFFFFFF)) if (write_mask != UINT32_C(0xFFFFFFFF))
{ {
// need to adjust the mask // need to adjust the mask
value = AndValues(value, Value::FromConstantU32(write_mask)); Value masked_value = AndValues(value, Value::FromConstantU32(write_mask));
if (g_settings.UsingPGXPCPUMode())
{
EmitFunctionCall(nullptr, &PGXP::CPU_MTC0, Value::FromConstantU32(cbi.instruction.bits), masked_value,
value);
}
value = std::move(masked_value);
}
else
{
if (g_settings.UsingPGXPCPUMode())
EmitFunctionCall(nullptr, &PGXP::CPU_MTC0, Value::FromConstantU32(cbi.instruction.bits), value, value);
} }
// changing SR[Isc] needs to update fastmem views // changing SR[Isc] needs to update fastmem views

View file

@ -650,17 +650,6 @@ void HostInterface::FixIncompatibleSettings(bool display_osd_messages)
} }
g_settings.gpu_pgxp_enable = false; g_settings.gpu_pgxp_enable = false;
} }
else if (g_settings.gpu_pgxp_cpu && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler)
{
if (display_osd_messages)
{
AddOSDMessage(
TranslateStdString("OSDMessage",
"PGXP CPU mode is incompatible with the recompiler, using Cached Interpreter instead."),
10.0f);
}
g_settings.cpu_execution_mode = CPUExecutionMode::CachedInterpreter;
}
} }
#ifndef WITH_MMAP_FASTMEM #ifndef WITH_MMAP_FASTMEM
@ -804,7 +793,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
} }
if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
(g_settings.gpu_pgxp_enable && g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling)) (g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling ||
g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu)))
{ {
if (g_settings.IsUsingCodeCache()) if (g_settings.IsUsingCodeCache())
{ {

View file

@ -233,6 +233,7 @@ struct Settings
} }
ALWAYS_INLINE bool UsingPGXPDepthBuffer() const { return gpu_pgxp_enable && gpu_pgxp_depth_buffer; } ALWAYS_INLINE bool UsingPGXPDepthBuffer() const { return gpu_pgxp_enable && gpu_pgxp_depth_buffer; }
ALWAYS_INLINE bool UsingPGXPCPUMode() const { return gpu_pgxp_enable && gpu_pgxp_cpu; }
ALWAYS_INLINE float GetPGXPDepthClearThreshold() const { return gpu_pgxp_depth_clear_threshold * 4096.0f; } ALWAYS_INLINE float GetPGXPDepthClearThreshold() const { return gpu_pgxp_depth_clear_threshold * 4096.0f; }
ALWAYS_INLINE void SetPGXPDepthClearThreshold(float value) { gpu_pgxp_depth_clear_threshold = value / 4096.0f; } ALWAYS_INLINE void SetPGXPDepthClearThreshold(float value) { gpu_pgxp_depth_clear_threshold = value / 4096.0f; }