Skip to content

Commit

Permalink
SPU Recompiler fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
Nekotekina committed Sep 6, 2015
1 parent ab1c05a commit 7e01c81
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 332 deletions.
570 changes: 301 additions & 269 deletions rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp

Large diffs are not rendered by default.

31 changes: 8 additions & 23 deletions rpcs3/Emu/Cell/SPUASMJITRecompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,28 @@ class spu_recompiler : public SPURecompilerBase
asmjit::X86GpVar* cpu;
asmjit::X86GpVar* ls;

// output:
asmjit::X86GpVar* pos;

// temporary:
asmjit::X86GpVar* addr;
asmjit::X86GpVar* qw0;
asmjit::X86GpVar* qw1;
asmjit::X86GpVar* qw2;
std::array<asmjit::X86XmmVar*, 10> vec;
std::array<asmjit::X86XmmVar*, 6> vec;

// labels:
asmjit::Label* labels; // array[0x10000]
asmjit::Label* jt; // jump table resolver
asmjit::Label* jt; // jump table resolver (uses *addr)
asmjit::Label* end; // function end (return *addr)

class XmmLink
{
friend class spu_recompiler;

asmjit::X86XmmVar*& m_alloc;
asmjit::X86XmmVar* xmm_var;
asmjit::X86XmmVar* const m_var;

XmmLink(asmjit::X86XmmVar*& xmm_var)
: m_alloc(xmm_var)
, xmm_var(xmm_var)
: m_var(xmm_var)
{
m_alloc = nullptr;
xmm_var = nullptr;
}

public:
Expand All @@ -64,24 +60,13 @@ class spu_recompiler : public SPURecompilerBase
XmmLink(const XmmLink&) = delete;

XmmLink(XmmLink&& right)
: m_alloc(right.m_alloc)
, xmm_var(right.xmm_var)
{
right.xmm_var = nullptr;
}

XmmLink& operator =(const XmmLink&) = delete;

XmmLink& operator =(XmmLink&& right) = delete;

~XmmLink()
: m_var(right.m_var)
{
if (xmm_var) m_alloc = xmm_var;
}

inline operator const asmjit::X86XmmVar&() const
{
return *xmm_var;
return *m_var;
}
};

Expand Down
101 changes: 84 additions & 17 deletions rpcs3/Emu/Cell/SPUAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@

const spu_opcode_table_t<spu_itype_t> g_spu_itype{ DEFINE_SPU_OPCODES(spu_itype::), spu_itype::UNK };

std::shared_ptr<spu_function_t> SPUDatabase::find(const be_t<u32>* data, u64 key, u32 max_size)
{
for (auto found = m_db.find(key); found != m_db.end(); found++)
{
if (found->second->size > max_size)
{
continue;
}

// Compare binary data explicitly (TODO: optimize)
if (std::equal(found->second->data.begin(), found->second->data.end(), data))
{
return found->second;
}
}

return nullptr;
}

SPUDatabase::SPUDatabase()
{
// TODO: load existing database associated with currently running executable
Expand All @@ -33,13 +52,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
const u64 key = entry | u64{ ls[entry / 4] } << 32;

// Try to find existing function in the database
for (auto found = m_db.find(key); found != m_db.end(); found++)
if (auto func = find(ls + entry / 4, key, max_limit - entry))
{
// Compare binary data explicitly (TODO: optimize)
if (std::equal(found->second->data.begin(), found->second->data.end(), ls + entry / 4))
{
return found->second;
}
return func;
}

// Initialize block entries with the function entry point
Expand All @@ -51,6 +66,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Set initial limit which will be narrowed later
u32 limit = max_limit;

// Minimal position of ila $SP,* instruction
u32 ila_sp_pos = max_limit;

// Find preliminary set of possible block entries (first pass), `start` is the current block address
for (u32 start = entry, pos = entry; pos < limit; pos += 4)
{
Expand All @@ -60,7 +78,15 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en

using namespace spu_itype;

if (start == pos) // Additional analysis at the beginning of the block (questionable)
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
limit = pos;
break;
}

// Additional analysis at the beginning of the block
if (start != entry && start == pos)
{
// Possible jump table
std::vector<u32> jt_abs, jt_rel;
Expand Down Expand Up @@ -128,17 +154,19 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
// Discard current block and abort the operation
limit = start;

break;
}
else if (op.opcode == 0) // Hack: special case (STOP 0)

if (op.opcode == 0) // Hack: special case (STOP 0)
{
limit = pos + 4;

break;
}
else if (type == BI) // Branch Indirect

if (type == BI || type == IRET) // Branch Indirect
{
if (type == IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos);

blocks.emplace(start); start = pos + 4;
}
else if (type == BR || type == BRA) // Branch Relative/Absolute
Expand All @@ -163,7 +191,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
// Branch to the next instruction and set link ("get next instruction address" idiom)

if (op.rt == 0) LOG_ERROR(SPU, "Suspicious instruction at [0x%05x]", pos);
if (op.rt == 0) LOG_ERROR(SPU, "[0x%05x] Branch-to-next with $LR", pos);
}
else
{
Expand All @@ -174,13 +202,15 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
limit = std::min<u32>(limit, target);
}

if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Function call without $LR", pos);
}
}
else if (type == BISL) // Branch Indirect and Set Link
else if (type == BISL || type == BISLED) // Branch Indirect and Set Link
{
// Nothing
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Indirect function call without $LR", pos);
}
else if (type == BRNZ || type == BRZ || type == BRHNZ || type == BRHZ) // Branch Relative if (Not) Zero Word/Halfword
else if (type == BRNZ || type == BRZ || type == BRHNZ || type == BRHZ) // Branch Relative if (Not) Zero (Half)word
{
const u32 target = spu_branch_target(pos, op.i16);

Expand All @@ -192,6 +222,40 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
blocks.emplace(target);
}
}
else if (type == BINZ || type == BIZ || type == BIHNZ || type == BIHZ) // Branch Indirect if (Not) Zero (Half)word
{
}
else if (type == HBR || type == HBRA || type == HBRR) // Hint for Branch
{
}
else if (type == STQA || type == STQD || type == STQR || type == STQX || type == FSCRWR || type == MTSPR || type == WRCH) // Store
{
}
else if (type == HEQ || type == HEQI || type == HGT || type == HGTI || type == HLGT || type == HLGTI) // Halt
{
}
else if (type == STOP || type == STOPD || type == NOP || type == LNOP || type == SYNC || type == DSYNC) // Miscellaneous
{
}
else // Other instructions (writing rt reg)
{
const u32 rt = type == SELB || type == SHUFB || type == MPYA || type == FNMS || type == FMA || type == FMS ? op.rc : op.rt;

// Analyse link register access
if (rt == 0)
{
}

// Analyse stack pointer access
if (rt == 1)
{
if (type == ILA && pos < ila_sp_pos)
{
// set minimal ila $SP,* instruction position
ila_sp_pos = pos;
}
}
}
}

// Find more function calls (second pass, questionable)
Expand Down Expand Up @@ -228,6 +292,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Prepare new function (set addr and size)
auto func = std::make_shared<spu_function_t>(entry, limit - entry);

// Copy function contents
func->data = { ls + entry / 4, ls + limit / 4 };

// Fill function block info
for (auto i = blocks.crbegin(); i != blocks.crend(); i++)
{
Expand Down Expand Up @@ -255,8 +322,8 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
}
}

// Copy function contents
func->data = { ls + entry / 4, ls + limit / 4 };
// Set whether the function can reset stack
func->does_reset_stack = ila_sp_pos < limit;

// Add function to the database
m_db.emplace(key, func);
Expand Down
6 changes: 6 additions & 0 deletions rpcs3/Emu/Cell/SPUAnalyser.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ struct spu_function_t
// jump table values (start addresses)
std::set<u32> jtable;

// whether ila $SP,* instruction found
bool does_reset_stack;

// pointer to the compiled function
spu_jit_func_t compiled = nullptr;

Expand All @@ -260,6 +263,9 @@ class SPUDatabase final
// All registered functions (uses addr and first instruction as a key)
std::unordered_multimap<u64, std::shared_ptr<spu_function_t>> m_db;

// For internal use
std::shared_ptr<spu_function_t> find(const be_t<u32>* data, u64 key, u32 max_size);

public:
SPUDatabase();
~SPUDatabase();
Expand Down
24 changes: 12 additions & 12 deletions rpcs3/Emu/Cell/SPUInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ void spu_interpreter::BIZ(SPUThread& spu, spu_opcode_t op)
if (spu.gpr[op.rt]._u32[3] == 0)
{
set_interrupt_status(spu, op);
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3], 0) - 4;
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4;
}
}

Expand All @@ -307,7 +307,7 @@ void spu_interpreter::BINZ(SPUThread& spu, spu_opcode_t op)
if (spu.gpr[op.rt]._u32[3] != 0)
{
set_interrupt_status(spu, op);
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3], 0) - 4;
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4;
}
}

Expand All @@ -316,7 +316,7 @@ void spu_interpreter::BIHZ(SPUThread& spu, spu_opcode_t op)
if (spu.gpr[op.rt]._u16[6] == 0)
{
set_interrupt_status(spu, op);
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3], 0) - 4;
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4;
}
}

Expand All @@ -325,13 +325,13 @@ void spu_interpreter::BIHNZ(SPUThread& spu, spu_opcode_t op)
if (spu.gpr[op.rt]._u16[6] != 0)
{
set_interrupt_status(spu, op);
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3], 0) - 4;
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4;
}
}

void spu_interpreter::STOPD(SPUThread& spu, spu_opcode_t op)
{
throw EXCEPTION("Unexpected instruction");
throw EXCEPTION("Unimplemented instruction");
}

void spu_interpreter::STQX(SPUThread& spu, spu_opcode_t op)
Expand All @@ -342,25 +342,25 @@ void spu_interpreter::STQX(SPUThread& spu, spu_opcode_t op)
void spu_interpreter::BI(SPUThread& spu, spu_opcode_t op)
{
set_interrupt_status(spu, op);
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3], 0) - 4;
spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4;
}

void spu_interpreter::BISL(SPUThread& spu, spu_opcode_t op)
{
set_interrupt_status(spu, op);
const u32 target = spu_branch_target(spu.gpr[op.ra]._u32[3], 0);
spu.gpr[op.rt] = v128::from32r(spu.pc + 4);
const u32 target = spu_branch_target(spu.gpr[op.ra]._u32[3]);
spu.gpr[op.rt] = v128::from32r(spu_branch_target(spu.pc + 4));
spu.pc = target - 4;
}

void spu_interpreter::IRET(SPUThread& spu, spu_opcode_t op)
{
throw EXCEPTION("Unexpected instruction");
throw EXCEPTION("Unimplemented instruction");
}

void spu_interpreter::BISLED(SPUThread& spu, spu_opcode_t op)
{
throw EXCEPTION("Unexpected instruction");
throw EXCEPTION("Unimplemented instruction");
}

void spu_interpreter::HBR(SPUThread& spu, spu_opcode_t op)
Expand Down Expand Up @@ -1022,7 +1022,7 @@ void spu_interpreter::LQA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter::BRASL(SPUThread& spu, spu_opcode_t op)
{
const u32 target = spu_branch_target(0, op.i16);
spu.gpr[op.rt] = v128::from32r(spu.pc + 4);
spu.gpr[op.rt] = v128::from32r(spu_branch_target(spu.pc + 4));
spu.pc = target - 4;
}

Expand All @@ -1039,7 +1039,7 @@ void spu_interpreter::FSMBI(SPUThread& spu, spu_opcode_t op)
void spu_interpreter::BRSL(SPUThread& spu, spu_opcode_t op)
{
const u32 target = spu_branch_target(spu.pc, op.i16);
spu.gpr[op.rt] = v128::from32r(spu.pc + 4);
spu.gpr[op.rt] = v128::from32r(spu_branch_target(spu.pc + 4));
spu.pc = target - 4;
}

Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/SPUOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ template<typename T> class spu_opcode_table_t
}
};

inline u32 spu_branch_target(u32 pc, s32 imm)
inline u32 spu_branch_target(u32 pc, s32 imm = 0)
{
return (pc + (imm << 2)) & 0x3fffc;
}
Loading

0 comments on commit 7e01c81

Please sign in to comment.