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);
break;
case InstructionOp::addi:
case InstructionOp::addiu:
result = Compile_addiu(cbi);
result = Compile_Add(cbi);
break;
case InstructionOp::funct:
@ -132,6 +133,11 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
result = Compile_MoveHiLo(cbi);
break;
case InstructionFunct::add:
case InstructionFunct::addu:
result = Compile_Add(cbi);
break;
case InstructionFunct::mult:
case InstructionFunct::multu:
result = Compile_Multiply(cbi);
@ -261,10 +267,10 @@ void CodeGenerator::ConvertValueSizeInPlace(Value* value, RegSize size, bool sig
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);
if (lhs.IsConstant() && rhs.IsConstant())
if (lhs.IsConstant() && rhs.IsConstant() && !set_flags)
{
// compile-time
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);
if (lhs.HasConstantValue(0))
if (lhs.HasConstantValue(0) && !set_flags)
{
EmitCopyValue(res.host_reg, rhs);
return res;
}
else if (rhs.HasConstantValue(0))
else if (rhs.HasConstantValue(0) && !set_flags)
{
EmitCopyValue(res.host_reg, lhs);
return res;
@ -301,7 +307,7 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
else
{
EmitCopyValue(res.host_reg, lhs);
EmitAdd(res.host_reg, rhs);
EmitAdd(res.host_reg, rhs, set_flags);
return res;
}
}
@ -903,7 +909,7 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi)
// rt <- mem[rs + sext(imm)]
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
Value address = AddValues(base, offset);
Value address = AddValues(base, offset, false);
Value result;
switch (cbi.instruction.op)
@ -942,7 +948,7 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi)
// mem[rs + sext(imm)] <- rt
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
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);
switch (cbi.instruction.op)
@ -999,6 +1005,52 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
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)
{
InstructionPrologue(cbi, 1);
@ -1052,7 +1104,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
{
// npc = pc + (sext(imm) << 2)
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
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)
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
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)
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 bool bgez = ConvertToBoolUnchecked(rt & u8(1));
@ -1118,18 +1170,4 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
InstructionEpilogue(cbi);
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

View file

@ -47,7 +47,7 @@ public:
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 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 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);
@ -76,6 +76,9 @@ public:
// Branching, generates two paths.
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();
void RestoreStackAfterCall(u32 adjust_size);
@ -130,7 +133,7 @@ public:
#endif
// 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);
Value ShlValues(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_Store(const CodeBlockInstruction& cbi);
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
bool Compile_Add(const CodeBlockInstruction& cbi);
bool Compile_Multiply(const CodeBlockInstruction& cbi);
bool Compile_Branch(const CodeBlockInstruction& cbi);
bool Compile_lui(const CodeBlockInstruction& cbi);
bool Compile_addiu(const CodeBlockInstruction& cbi);
Core* m_cpu;
JitCodeBuffer* m_code_buffer;

View file

@ -44,6 +44,10 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool relative, c
{
Panic("Not implemented");
}
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
{
Panic("Not implemented");
}
#endif
} // 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
// 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_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());
@ -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)
{
@ -1712,6 +1713,28 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool always_link
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
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);
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
// 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);
if (clear)
{
m_load_delay_register = Reg::count;
m_load_delay_value.ReleaseAndClear();
}
}
void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)

View file

@ -252,7 +252,7 @@ public:
void WriteLoadDelayToCPU(bool clear);
/// 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 InvalidateGuestRegister(Reg guest_reg);

View file

@ -78,6 +78,11 @@ void Thunks::UpdateLoadDelay(Core* cpu)
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)
{
cpu->m_cop0_regs.BadVaddr = address;

View file

@ -21,6 +21,7 @@ public:
static bool WriteMemoryWord(Core* cpu, u32 address, u32 value);
static bool InterpretInstruction(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);
};