CPU/Recompiler: Implement add/addu/addi

This commit is contained in:
Connor McLaughlin 2019-11-23 00:26:56 +10:00
parent 641e68db95
commit f14ad1d3c4
8 changed files with 113 additions and 33 deletions

View file

@ -105,8 +105,9 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
result = Compile_lui(cbi); result = Compile_lui(cbi);
break; break;
case InstructionOp::addi:
case InstructionOp::addiu: case InstructionOp::addiu:
result = Compile_addiu(cbi); result = Compile_Add(cbi);
break; break;
case InstructionOp::funct: case InstructionOp::funct:
@ -132,6 +133,11 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
result = Compile_MoveHiLo(cbi); result = Compile_MoveHiLo(cbi);
break; break;
case InstructionFunct::add:
case InstructionFunct::addu:
result = Compile_Add(cbi);
break;
case InstructionFunct::mult: case InstructionFunct::mult:
case InstructionFunct::multu: case InstructionFunct::multu:
result = Compile_Multiply(cbi); result = Compile_Multiply(cbi);
@ -261,10 +267,10 @@ void CodeGenerator::ConvertValueSizeInPlace(Value* value, RegSize size, bool sig
value->size = size; value->size = size;
} }
Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs) Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs, bool set_flags)
{ {
DebugAssert(lhs.size == rhs.size); DebugAssert(lhs.size == rhs.size);
if (lhs.IsConstant() && rhs.IsConstant()) if (lhs.IsConstant() && rhs.IsConstant() && !set_flags)
{ {
// compile-time // compile-time
u64 new_cv = lhs.constant_value + rhs.constant_value; u64 new_cv = lhs.constant_value + rhs.constant_value;
@ -288,12 +294,12 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
} }
Value res = m_register_cache.AllocateScratch(lhs.size); Value res = m_register_cache.AllocateScratch(lhs.size);
if (lhs.HasConstantValue(0)) if (lhs.HasConstantValue(0) && !set_flags)
{ {
EmitCopyValue(res.host_reg, rhs); EmitCopyValue(res.host_reg, rhs);
return res; return res;
} }
else if (rhs.HasConstantValue(0)) else if (rhs.HasConstantValue(0) && !set_flags)
{ {
EmitCopyValue(res.host_reg, lhs); EmitCopyValue(res.host_reg, lhs);
return res; return res;
@ -301,7 +307,7 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
else else
{ {
EmitCopyValue(res.host_reg, lhs); EmitCopyValue(res.host_reg, lhs);
EmitAdd(res.host_reg, rhs); EmitAdd(res.host_reg, rhs, set_flags);
return res; return res;
} }
} }
@ -903,7 +909,7 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi)
// rt <- mem[rs + sext(imm)] // rt <- mem[rs + sext(imm)]
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs); Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32()); Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
Value address = AddValues(base, offset); Value address = AddValues(base, offset, false);
Value result; Value result;
switch (cbi.instruction.op) switch (cbi.instruction.op)
@ -942,7 +948,7 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi)
// mem[rs + sext(imm)] <- rt // mem[rs + sext(imm)] <- rt
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs); Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32()); Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
Value address = AddValues(base, offset); Value address = AddValues(base, offset, false);
Value value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt); Value value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt);
switch (cbi.instruction.op) switch (cbi.instruction.op)
@ -999,6 +1005,52 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
return true; return true;
} }
bool CodeGenerator::Compile_Add(const CodeBlockInstruction& cbi)
{
InstructionPrologue(cbi, 1);
const bool check_overflow =
(cbi.instruction.op == InstructionOp::addi ||
(cbi.instruction.op == InstructionOp::funct && cbi.instruction.r.funct == InstructionFunct::add));
Value lhs, rhs;
Reg dest;
switch (cbi.instruction.op)
{
case InstructionOp::addi:
case InstructionOp::addiu:
{
// rt <- rs + sext(imm)
dest = cbi.instruction.i.rt;
lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
rhs = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
}
break;
case InstructionOp::funct:
{
Assert(cbi.instruction.r.funct == InstructionFunct::add || cbi.instruction.r.funct == InstructionFunct::addu);
dest = cbi.instruction.r.rd;
lhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
rhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
}
break;
default:
UnreachableCode();
return false;
}
Value result = AddValues(lhs, rhs, check_overflow);
if (check_overflow)
EmitRaiseException(Exception::Ov, Condition::Overflow);
m_register_cache.WriteGuestRegister(dest, std::move(result));
InstructionEpilogue(cbi);
return true;
}
bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi) bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi)
{ {
InstructionPrologue(cbi, 1); InstructionPrologue(cbi, 1);
@ -1052,7 +1104,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
{ {
// npc = pc + (sext(imm) << 2) // npc = pc + (sext(imm) << 2)
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
// branch <- rs op rt // branch <- rs op rt
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true); Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
@ -1069,7 +1121,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
{ {
// npc = pc + (sext(imm) << 2) // npc = pc + (sext(imm) << 2)
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
// branch <- rs op 0 // branch <- rs op 0
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true); Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
@ -1085,7 +1137,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
{ {
// npc = pc + (sext(imm) << 2) // npc = pc + (sext(imm) << 2)
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
const u8 rt = static_cast<u8>(cbi.instruction.i.rt.GetValue()); const u8 rt = static_cast<u8>(cbi.instruction.i.rt.GetValue());
const bool bgez = ConvertToBoolUnchecked(rt & u8(1)); const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
@ -1118,18 +1170,4 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
InstructionEpilogue(cbi); InstructionEpilogue(cbi);
return true; return true;
} }
bool CodeGenerator::Compile_addiu(const CodeBlockInstruction& cbi)
{
InstructionPrologue(cbi, 1);
// rt <- rs + sext(imm)
m_register_cache.WriteGuestRegister(cbi.instruction.i.rt,
AddValues(m_register_cache.ReadGuestRegister(cbi.instruction.i.rs),
Value::FromConstantU32(cbi.instruction.i.imm_sext32())));
InstructionEpilogue(cbi);
return true;
}
} // namespace CPU::Recompiler } // namespace CPU::Recompiler

View file

@ -47,7 +47,7 @@ public:
void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size); void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size); void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
void EmitCopyValue(HostReg to_reg, const Value& value); void EmitCopyValue(HostReg to_reg, const Value& value);
void EmitAdd(HostReg to_reg, const Value& value); void EmitAdd(HostReg to_reg, const Value& value, bool set_flags);
void EmitSub(HostReg to_reg, const Value& value); void EmitSub(HostReg to_reg, const Value& value);
void EmitCmp(HostReg to_reg, const Value& value); void EmitCmp(HostReg to_reg, const Value& value);
void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply); void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply);
@ -76,6 +76,9 @@ public:
// Branching, generates two paths. // Branching, generates two paths.
void EmitBranch(Condition condition, Reg lr_reg, bool always_link, Value&& branch_target); void EmitBranch(Condition condition, Reg lr_reg, bool always_link, Value&& branch_target);
// Raising exception if condition is true.
void EmitRaiseException(Exception excode, Condition condition = Condition::Always);
u32 PrepareStackForCall(); u32 PrepareStackForCall();
void RestoreStackAfterCall(u32 adjust_size); void RestoreStackAfterCall(u32 adjust_size);
@ -130,7 +133,7 @@ public:
#endif #endif
// Value ops // Value ops
Value AddValues(const Value& lhs, const Value& rhs); Value AddValues(const Value& lhs, const Value& rhs, bool set_flags);
std::pair<Value, Value> MulValues(const Value& lhs, const Value& rhs, bool signed_multiply); std::pair<Value, Value> MulValues(const Value& lhs, const Value& rhs, bool signed_multiply);
Value ShlValues(const Value& lhs, const Value& rhs); Value ShlValues(const Value& lhs, const Value& rhs);
Value ShrValues(const Value& lhs, const Value& rhs); Value ShrValues(const Value& lhs, const Value& rhs);
@ -175,10 +178,10 @@ private:
bool Compile_Load(const CodeBlockInstruction& cbi); bool Compile_Load(const CodeBlockInstruction& cbi);
bool Compile_Store(const CodeBlockInstruction& cbi); bool Compile_Store(const CodeBlockInstruction& cbi);
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi); bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
bool Compile_Add(const CodeBlockInstruction& cbi);
bool Compile_Multiply(const CodeBlockInstruction& cbi); bool Compile_Multiply(const CodeBlockInstruction& cbi);
bool Compile_Branch(const CodeBlockInstruction& cbi); bool Compile_Branch(const CodeBlockInstruction& cbi);
bool Compile_lui(const CodeBlockInstruction& cbi); bool Compile_lui(const CodeBlockInstruction& cbi);
bool Compile_addiu(const CodeBlockInstruction& cbi);
Core* m_cpu; Core* m_cpu;
JitCodeBuffer* m_code_buffer; JitCodeBuffer* m_code_buffer;

View file

@ -44,6 +44,10 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool relative, c
{ {
Panic("Not implemented"); Panic("Not implemented");
} }
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
{
Panic("Not implemented");
}
#endif #endif
} // namespace CPU::Recompiler } // namespace CPU::Recompiler

View file

@ -186,7 +186,7 @@ void CodeGenerator::EmitExceptionExit()
// the interpreter load delay might have its own value, but we'll overwrite it here anyway // the interpreter load delay might have its own value, but we'll overwrite it here anyway
// technically RaiseException() and FlushPipeline() have already been called, but that should be okay // technically RaiseException() and FlushPipeline() have already been called, but that should be okay
m_register_cache.FlushLoadDelayForException(); m_register_cache.FlushLoadDelay(false);
m_register_cache.PopCalleeSavedRegisters(false); m_register_cache.PopCalleeSavedRegisters(false);
m_emit->ret(); m_emit->ret();
@ -339,7 +339,7 @@ void CodeGenerator::EmitCopyValue(HostReg to_reg, const Value& value)
} }
} }
void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value) void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags)
{ {
DebugAssert(value.IsConstant() || value.IsInHostRegister()); DebugAssert(value.IsConstant() || value.IsInHostRegister());
@ -1601,7 +1601,8 @@ void CodeGenerator::EmitDelaySlotUpdate(bool skip_check_for_delay, bool skip_che
} }
} }
static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const Xbyak::Label& label) template<typename T>
static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const T& label)
{ {
switch (condition) switch (condition)
{ {
@ -1712,6 +1713,28 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool always_link
m_emit->L(skip_branch); m_emit->L(skip_branch);
} }
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
{
if (condition == Condition::Always)
{
// no need to use far code if we're always raising the exception
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(),
Value::FromConstantU8(static_cast<u8>(excode)));
m_register_cache.FlushAllGuestRegisters(true, true);
m_register_cache.FlushLoadDelay(true);
return;
}
const void* far_code_ptr = GetCurrentFarCodePointer();
EmitConditionalJump(condition, false, m_emit, far_code_ptr);
SwitchToFarCode();
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(),
Value::FromConstantU8(static_cast<u8>(excode)));
EmitExceptionExit();
SwitchToNearCode();
}
#if 0 #if 0
class ThunkGenerator class ThunkGenerator
{ {

View file

@ -570,7 +570,7 @@ void RegisterCache::WriteLoadDelayToCPU(bool clear)
} }
} }
void RegisterCache::FlushLoadDelayForException() void RegisterCache::FlushLoadDelay(bool clear)
{ {
Assert(m_next_load_delay_register == Reg::count); Assert(m_next_load_delay_register == Reg::count);
if (m_load_delay_register == Reg::count) if (m_load_delay_register == Reg::count)
@ -579,6 +579,12 @@ void RegisterCache::FlushLoadDelayForException()
// if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next // if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next
// non-exception-raised path. TODO: push/pop whole state would avoid this issue // non-exception-raised path. TODO: push/pop whole state would avoid this issue
m_code_generator.EmitStoreGuestRegister(m_load_delay_register, m_load_delay_value); m_code_generator.EmitStoreGuestRegister(m_load_delay_register, m_load_delay_value);
if (clear)
{
m_load_delay_register = Reg::count;
m_load_delay_value.ReleaseAndClear();
}
} }
void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty) void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)

View file

@ -252,7 +252,7 @@ public:
void WriteLoadDelayToCPU(bool clear); void WriteLoadDelayToCPU(bool clear);
/// Flushes the load delay, i.e. writes it to the destination register. /// Flushes the load delay, i.e. writes it to the destination register.
void FlushLoadDelayForException(); void FlushLoadDelay(bool clear);
void FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty); void FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty);
void InvalidateGuestRegister(Reg guest_reg); void InvalidateGuestRegister(Reg guest_reg);

View file

@ -78,6 +78,11 @@ void Thunks::UpdateLoadDelay(Core* cpu)
cpu->UpdateLoadDelay(); cpu->UpdateLoadDelay();
} }
void Thunks::RaiseException(Core* cpu, u8 excode)
{
cpu->RaiseException(static_cast<Exception>(excode));
}
void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch) void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch)
{ {
cpu->m_cop0_regs.BadVaddr = address; cpu->m_cop0_regs.BadVaddr = address;

View file

@ -21,6 +21,7 @@ public:
static bool WriteMemoryWord(Core* cpu, u32 address, u32 value); static bool WriteMemoryWord(Core* cpu, u32 address, u32 value);
static bool InterpretInstruction(Core* cpu); static bool InterpretInstruction(Core* cpu);
static void UpdateLoadDelay(Core* cpu); static void UpdateLoadDelay(Core* cpu);
static void RaiseException(Core* cpu, u8 excode);
static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch); static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch);
}; };