mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 18:40:36 +00:00
Partial commit: Cell (deletions)
This commit is contained in:
parent
c4e99dbdb2
commit
4cf41305e0
@ -1,11 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "PPCDecoder.h"
|
||||
|
||||
u32 PPCDecoder::DecodeMemory(const u32 address)
|
||||
{
|
||||
u32 instr = vm::ps3::read32(address);
|
||||
Decode(instr);
|
||||
|
||||
return sizeof(u32);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "Emu/CPU/CPUDecoder.h"
|
||||
#include "PPCInstrTable.h"
|
||||
|
||||
class PPCDecoder : public CPUDecoder
|
||||
{
|
||||
public:
|
||||
virtual void Decode(const u32 code) = 0;
|
||||
|
||||
virtual u32 DecodeMemory(const u32 address);
|
||||
|
||||
virtual ~PPCDecoder() = default;
|
||||
};
|
||||
|
||||
|
||||
template<typename TO, uint from, uint to>
|
||||
static InstrList<(1 << (CodeField<from, to>::size)), TO>* new_list(const CodeField<from, to>& func, InstrCaller<TO>* error_func = nullptr)
|
||||
{
|
||||
return new InstrList<(1 << (CodeField<from, to>::size)), TO>(func, error_func);
|
||||
}
|
||||
|
||||
template<int count, typename TO, uint from, uint to>
|
||||
static InstrList<(1 << (CodeField<from, to>::size)), TO>* new_list(InstrList<count, TO>* parent, int opcode, const CodeField<from, to>& func, InstrCaller<TO>* error_func = nullptr)
|
||||
{
|
||||
return connect_list(parent, new InstrList<(1 << (CodeField<from, to>::size)), TO>(func, error_func), opcode);
|
||||
}
|
||||
|
||||
template<int count, typename TO, uint from, uint to>
|
||||
static InstrList<(1 << (CodeField<from, to>::size)), TO>* new_list(InstrList<count, TO>* parent, const CodeField<from, to>& func, InstrCaller<TO>* error_func = nullptr)
|
||||
{
|
||||
return connect_list(parent, new InstrList<(1 << (CodeField<from, to>::size)), TO>(func, error_func));
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
#pragma once
|
||||
#include "Emu/CPU/CPUInstrTable.h"
|
||||
|
||||
enum CodeFieldType
|
||||
{
|
||||
FIELD_IMM,
|
||||
FIELD_R_GPR,
|
||||
FIELD_R_FPR,
|
||||
FIELD_R_VPR,
|
||||
FIELD_R_CR,
|
||||
FIELD_BRANCH,
|
||||
};
|
||||
|
||||
template<uint from, uint to=from>
|
||||
class CodeField : public CodeFieldBase
|
||||
{
|
||||
static_assert(from <= to, "too big 'from' value");
|
||||
static_assert(to <= 31, "too big 'to' value");
|
||||
|
||||
public:
|
||||
CodeField(CodeFieldType type = FIELD_IMM) : CodeFieldBase(type)
|
||||
{
|
||||
}
|
||||
|
||||
static const u32 size = to - from + 1;
|
||||
static const u32 shift = 31 - to;
|
||||
static const u32 mask = ((1ULL << ((to - from) + 1)) - 1) << shift;
|
||||
|
||||
static force_inline void encode(u32& data, u32 value)
|
||||
{
|
||||
data &= ~mask;
|
||||
data |= (value << shift) & mask;
|
||||
}
|
||||
|
||||
static force_inline u32 decode(u32 data)
|
||||
{
|
||||
return (data & mask) >> shift;
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const
|
||||
{
|
||||
return decode(data);
|
||||
}
|
||||
|
||||
virtual void operator()(u32& data, u32 value) const
|
||||
{
|
||||
encode(data, value);
|
||||
}
|
||||
};
|
||||
|
||||
static CodeField<0, 31> GetCode;
|
||||
|
||||
template<uint from1, uint to1, uint from2, uint to2 = from2, uint offset = 0>
|
||||
class DoubleCodeField : public CodeField<from1, to1>
|
||||
{
|
||||
static_assert(from2 <= to2, "too big from2 value");
|
||||
static_assert(to2 <= 31, "too big to2 value");
|
||||
|
||||
public:
|
||||
DoubleCodeField(CodeFieldType type = FIELD_IMM) : CodeField<from1, to1>(type)
|
||||
{
|
||||
}
|
||||
|
||||
static const u32 shift2 = 31 - to2;
|
||||
static const u32 mask2 = ((1 << ((to2 - from2) + 1)) - 1) << shift2;
|
||||
|
||||
static force_inline void encode(u32& data, u32 value)
|
||||
{
|
||||
data &= ~(CodeField<from1, to1>::mask | mask2);
|
||||
data |= ((value << CodeField<from1, to1>::shift) & CodeField<from1, to1>::mask) | (((value >> offset) << shift2) & mask2);
|
||||
}
|
||||
|
||||
static force_inline u32 decode(u32 data)
|
||||
{
|
||||
return ((data & CodeField<from1, to1>::mask) >> CodeField<from1, to1>::shift) | (((data & mask2) >> shift2) << offset);
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const
|
||||
{
|
||||
return decode(data);
|
||||
}
|
||||
|
||||
virtual void operator()(u32& data, u32 value) const
|
||||
{
|
||||
encode(data, value);
|
||||
}
|
||||
};
|
||||
|
||||
template<uint from, uint to = from, uint _size = to - from + 1>
|
||||
class CodeFieldSigned : public CodeField<from, to>
|
||||
{
|
||||
public:
|
||||
CodeFieldSigned(CodeFieldType type = FIELD_IMM) : CodeField<from, to>(type)
|
||||
{
|
||||
}
|
||||
|
||||
static const int size = _size;
|
||||
|
||||
static force_inline u32 decode(u32 data)
|
||||
{
|
||||
return sign<size>((data & CodeField<from, to>::mask) >> CodeField<from, to>::shift);
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const
|
||||
{
|
||||
return decode(data);
|
||||
}
|
||||
};
|
||||
|
||||
template<uint from, uint to = from, uint _offset = 0>
|
||||
class CodeFieldOffset : public CodeField<from, to>
|
||||
{
|
||||
static const int offset = _offset;
|
||||
|
||||
public:
|
||||
CodeFieldOffset(CodeFieldType type = FIELD_IMM) : CodeField<from, to>(type)
|
||||
{
|
||||
}
|
||||
|
||||
static force_inline u32 decode(u32 data)
|
||||
{
|
||||
return ((data & CodeField<from, to>::mask) >> CodeField<from, to>::shift) << offset;
|
||||
}
|
||||
|
||||
static force_inline void encode(u32& data, u32 value)
|
||||
{
|
||||
data &= ~CodeField<from, to>::mask;
|
||||
data |= ((value >> offset) << CodeField<from, to>::shift) & CodeField<from, to>::mask;
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const
|
||||
{
|
||||
return decode(data);
|
||||
}
|
||||
|
||||
virtual void operator ()(u32& data, u32 value) const
|
||||
{
|
||||
return encode(data, value);
|
||||
}
|
||||
};
|
||||
|
||||
template<uint from, uint to = from, uint _offset = 0, uint size = to - from + 1>
|
||||
class CodeFieldSignedOffset : public CodeFieldSigned<from, to, size>
|
||||
{
|
||||
static const int offset = _offset;
|
||||
|
||||
public:
|
||||
CodeFieldSignedOffset(CodeFieldType type = FIELD_IMM) : CodeFieldSigned<from, to, size>(type)
|
||||
{
|
||||
}
|
||||
|
||||
static force_inline u32 decode(u32 data)
|
||||
{
|
||||
return sign<size>((data & CodeField<from, to>::mask) >> CodeField<from, to>::shift) << offset;
|
||||
}
|
||||
|
||||
static force_inline void encode(u32& data, u32 value)
|
||||
{
|
||||
data &= ~CodeField<from, to>::mask;
|
||||
data |= ((value >> offset) << CodeField<from, to>::shift) & CodeField<from, to>::mask;
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const
|
||||
{
|
||||
return decode(data);
|
||||
}
|
||||
|
||||
virtual void operator ()(u32& data, u32 value) const
|
||||
{
|
||||
return encode(data, value);
|
||||
}
|
||||
};
|
@ -1,110 +0,0 @@
|
||||
#if 0
|
||||
#include "stdafx.h"
|
||||
#include "PPCThreadManager.h"
|
||||
#include "PPUThread.h"
|
||||
#include "SPUThread.h"
|
||||
#include "RawSPUThread.h"
|
||||
|
||||
PPCThreadManager::PPCThreadManager() :
|
||||
m_raw_spu_num(0)
|
||||
{
|
||||
}
|
||||
|
||||
PPCThreadManager::~PPCThreadManager()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void PPCThreadManager::Close()
|
||||
{
|
||||
while(m_threads.size()) RemoveThread(m_threads[0]->GetId());
|
||||
}
|
||||
|
||||
PPCThread& PPCThreadManager::AddThread(PPCThreadType type)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mtx_thread);
|
||||
|
||||
PPCThread* new_thread;
|
||||
char* name;
|
||||
switch(type)
|
||||
{
|
||||
case PPC_THREAD_PPU: new_thread = new PPUThread(); name = "PPU"; break;
|
||||
case PPC_THREAD_SPU: new_thread = new SPUThread(); name = "SPU"; break;
|
||||
case PPC_THREAD_RAW_SPU: new_thread = new RawSPUThread(m_raw_spu_num++); name = "RawSPU"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
|
||||
new_thread->SetId(Emu.GetIdManager().GetNewID(fmt::Format("%s Thread", name), new_thread));
|
||||
|
||||
m_threads.push_back(new_thread);
|
||||
wxGetApp().SendDbgCommand(DID_CREATE_THREAD, new_thread);
|
||||
|
||||
return *new_thread;
|
||||
}
|
||||
|
||||
void PPCThreadManager::RemoveThread(const u32 id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mtx_thread);
|
||||
|
||||
for(u32 i=0; i<m_threads.size(); ++i)
|
||||
{
|
||||
if(m_threads[i]->m_wait_thread_id == id)
|
||||
{
|
||||
m_threads[i]->Wait(false);
|
||||
m_threads[i]->m_wait_thread_id = -1;
|
||||
}
|
||||
|
||||
if(m_threads[i]->GetId() != id) continue;
|
||||
|
||||
PPCThread* thr = m_threads[i];
|
||||
wxGetApp().SendDbgCommand(DID_REMOVE_THREAD, thr);
|
||||
if(thr->IsAlive())
|
||||
{
|
||||
thr->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
thr->Close();
|
||||
delete thr;
|
||||
}
|
||||
|
||||
|
||||
m_threads.erase(m_threads.begin() + i);
|
||||
i--;
|
||||
}
|
||||
|
||||
Emu.GetIdManager().RemoveID(id, false);
|
||||
Emu.CheckStatus();
|
||||
}
|
||||
|
||||
s32 PPCThreadManager::GetThreadNumById(PPCThreadType type, u32 id)
|
||||
{
|
||||
s32 num = 0;
|
||||
|
||||
for(u32 i=0; i<m_threads.size(); ++i)
|
||||
{
|
||||
if(m_threads[i]->GetId() == id) return num;
|
||||
if(m_threads[i]->GetType() == type) num++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
PPCThread* PPCThreadManager::GetThread(u32 id)
|
||||
{
|
||||
for(u32 i=0; i<m_threads.size(); ++i)
|
||||
{
|
||||
if(m_threads[i]->GetId() == id) return m_threads[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PPCThreadManager::Exec()
|
||||
{
|
||||
for(u32 i=0; i<m_threads.size(); ++i)
|
||||
{
|
||||
m_threads[i]->Exec();
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#include "PPCThread.h"
|
||||
|
||||
enum PPCThreadType
|
||||
{
|
||||
PPC_THREAD_PPU,
|
||||
PPC_THREAD_SPU,
|
||||
PPC_THREAD_RAW_SPU
|
||||
};
|
||||
|
||||
class PPCThreadManager
|
||||
{
|
||||
//IdManager m_threads_id;
|
||||
//ArrayF<PPUThread> m_ppu_threads;
|
||||
//ArrayF<SPUThread> m_spu_threads;
|
||||
std::vector<PPCThread *> m_threads;
|
||||
std::mutex m_mtx_thread;
|
||||
wxSemaphore m_sem_task;
|
||||
u32 m_raw_spu_num;
|
||||
|
||||
public:
|
||||
PPCThreadManager();
|
||||
~PPCThreadManager();
|
||||
|
||||
void Close();
|
||||
|
||||
PPCThread& AddThread(PPCThreadType type);
|
||||
void RemoveThread(const u32 id);
|
||||
|
||||
std::vector<PPCThread *>& GetThreads() { return m_threads; }
|
||||
s32 GetThreadNumById(PPCThreadType type, u32 id);
|
||||
PPCThread* GetThread(u32 id);
|
||||
//IdManager& GetIDs() {return m_threads_id;}
|
||||
|
||||
void Exec();
|
||||
void Task();
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Cell/PPUOpcodes.h"
|
||||
#include "Emu/Cell/PPCDecoder.h"
|
||||
#include "PPUInstrTable.h"
|
||||
|
||||
class PPUDecoder : public PPCDecoder
|
||||
{
|
||||
PPUOpcodes* m_op;
|
||||
|
||||
public:
|
||||
PPUDecoder(PPUOpcodes* op) : m_op(op)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PPUDecoder()
|
||||
{
|
||||
delete m_op;
|
||||
}
|
||||
|
||||
virtual void Decode(const u32 code)
|
||||
{
|
||||
(*PPU_instr::main_list)(m_op, code);
|
||||
}
|
||||
};
|
@ -1,704 +0,0 @@
|
||||
#pragma once
|
||||
#include "PPCInstrTable.h"
|
||||
#include "PPCDecoder.h"
|
||||
#include "PPUOpcodes.h"
|
||||
|
||||
namespace PPU_instr
|
||||
{
|
||||
namespace fields
|
||||
{
|
||||
//This field is used in rotate instructions to specify the first 1 bit of a 64-bit mask
|
||||
static DoubleCodeField<21, 25, 26, 26, 5> mb;
|
||||
|
||||
//This field is used in rotate instructions to specify the last 1 bit of a 64-bit mask
|
||||
static DoubleCodeField<21, 25, 26, 26, 5> me;
|
||||
|
||||
//This field is used to specify a shift amount
|
||||
static DoubleCodeField<16, 20, 30, 30, 5> sh;
|
||||
|
||||
//This field is used to specify a special-purpose register for the mtspr and mfspr instructions
|
||||
static CodeField<11, 20> SPR;
|
||||
|
||||
//
|
||||
static CodeField<6, 10> VS(FIELD_R_VPR);
|
||||
|
||||
//
|
||||
static CodeField<6, 10> VD(FIELD_R_VPR);
|
||||
|
||||
//
|
||||
static CodeField<11, 15> VA(FIELD_R_VPR);
|
||||
|
||||
//
|
||||
static CodeField<16, 20> VB(FIELD_R_VPR);
|
||||
|
||||
//
|
||||
static CodeField<21, 25> VC(FIELD_R_VPR);
|
||||
|
||||
//
|
||||
static CodeField<11, 15> VUIMM;
|
||||
|
||||
//
|
||||
static CodeFieldSigned<11, 15> VSIMM;
|
||||
|
||||
//
|
||||
static CodeField<22, 25> VSH;
|
||||
|
||||
//This field is used to specify a GPR to be used as a destination
|
||||
static CodeField<6, 10> RD(FIELD_R_GPR);
|
||||
|
||||
//This field is used to specify a GPR to be used as a source
|
||||
static CodeField<6, 10> RS(FIELD_R_GPR);
|
||||
|
||||
//This field is used to specify a GPR to be used as a source or destination
|
||||
static CodeField<11, 15> RA(FIELD_R_GPR);
|
||||
|
||||
//This field is used to specify a GPR to be used as a source
|
||||
static CodeField<16, 20> RB(FIELD_R_GPR);
|
||||
|
||||
//This field is used to specify the number of bytes to move in an immediate string load or store
|
||||
static CodeField<16, 20> NB;
|
||||
|
||||
//This field is used to specify one of the CR fields, or one of the FPSCR fields, as a destination
|
||||
static CodeField<6, 8> CRFD(FIELD_R_CR);
|
||||
|
||||
//This field is used to specify one of the CR fields, or one of the FPSCR fields, as a source
|
||||
static CodeField<11, 13> CRFS(FIELD_R_CR);
|
||||
|
||||
//This field is used to specify a bit in the CR to be used as a source
|
||||
static CodeField<11, 15> CRBA(FIELD_R_CR);
|
||||
|
||||
//This field is used to specify a bit in the CR to be used as a source
|
||||
static CodeField<16, 20> CRBB(FIELD_R_CR);
|
||||
|
||||
//This field is used to specify a bit in the CR, or in the FPSCR, as the destination of the result of an instruction
|
||||
static CodeField<6, 10> CRBD(FIELD_R_CR);
|
||||
|
||||
//This field is used to specify options for the branch conditional instructions
|
||||
static CodeField<6, 10> BO;
|
||||
|
||||
//This field is used to specify a bit in the CR to be used as the condition of a branch conditional instruction
|
||||
static CodeField<11, 15> BI;
|
||||
|
||||
//Immediate field specifying a 14-bit signed two's complement branch displacement that is concatenated on the
|
||||
//right with '00' and sign-extended to 64 bits.
|
||||
static CodeFieldSigned<16, 31> BD(FIELD_BRANCH);
|
||||
|
||||
//
|
||||
static CodeField<19, 20> BH;
|
||||
|
||||
//
|
||||
static CodeField<11, 13> BFA;
|
||||
|
||||
//Field used by the optional data stream variant of the dcbt instruction.
|
||||
static CodeField<9, 10> TH;
|
||||
|
||||
//This field is used to specify the conditions on which to trap
|
||||
static CodeField<6, 10> TO;
|
||||
|
||||
//
|
||||
static CodeField<21, 25> MB;
|
||||
|
||||
//
|
||||
static CodeField<26, 30> ME;
|
||||
|
||||
//This field is used to specify a shift amount
|
||||
static CodeField<16, 20> SH;
|
||||
|
||||
/*
|
||||
Absolute address bit.
|
||||
0 The immediate field represents an address relative to the current instruction address (CIA). (For more
|
||||
information on the CIA, see Table 8-3.) The effective (logical) address of the branch is either the sum
|
||||
of the LI field sign-extended to 64 bits and the address of the branch instruction or the sum of the BD
|
||||
field sign-extended to 64 bits and the address of the branch instruction.
|
||||
1 The immediate field represents an absolute address. The effective address (EA) of the branch is the
|
||||
LI field sign-extended to 64 bits or the BD field sign-extended to 64 bits.
|
||||
*/
|
||||
static CodeField<30> AA;
|
||||
|
||||
//
|
||||
static CodeFieldSignedOffset<6, 29, 2> LL(FIELD_BRANCH);
|
||||
/*
|
||||
Link bit.
|
||||
0 Does not update the link register (LR).
|
||||
1 Updates the LR. If the instruction is a branch instruction, the address of the instruction following the
|
||||
branch instruction is placed into the LR.
|
||||
*/
|
||||
static CodeField<31> LK;
|
||||
|
||||
//This field is used for extended arithmetic to enable setting OV and SO in the XER
|
||||
static CodeField<21> OE;
|
||||
|
||||
//Field used to specify whether an integer compare instruction is to compare 64-bit numbers or 32-bit numbers
|
||||
static CodeField<10> L_10;
|
||||
static CodeField<6> L_6;
|
||||
static CodeField<9, 10> L_9_10;
|
||||
static CodeField<11> L_11;
|
||||
//
|
||||
static CodeField<16, 19> I;
|
||||
|
||||
//
|
||||
static CodeField<16, 27> DQ;
|
||||
|
||||
//This field is used to specify an FPR as the destination
|
||||
static CodeField<6, 10> FRD;
|
||||
|
||||
//This field is used to specify an FPR as a source
|
||||
static CodeField<6, 10> FRS;
|
||||
|
||||
//
|
||||
static CodeField<7, 14> FM;
|
||||
|
||||
//This field is used to specify an FPR as a source
|
||||
static CodeField<11, 15> FRA(FIELD_R_FPR);
|
||||
|
||||
//This field is used to specify an FPR as a source
|
||||
static CodeField<16, 20> FRB(FIELD_R_FPR);
|
||||
|
||||
//This field is used to specify an FPR as a source
|
||||
static CodeField<21, 25> FRC(FIELD_R_FPR);
|
||||
|
||||
//This field mask is used to identify the CR fields that are to be updated by the mtcrf instruction.
|
||||
static CodeField<12, 19> CRM;
|
||||
|
||||
// This field is used to identify the system call level
|
||||
static CodeField<20, 26> LEV;
|
||||
|
||||
//Immediate field specifying a 16-bit signed two's complement integer that is sign-extended to 64 bits
|
||||
static CodeFieldSigned<16, 31> D;
|
||||
|
||||
//
|
||||
static CodeFieldSignedOffset<16, 29, 2> DS;
|
||||
|
||||
//This immediate field is used to specify a 16-bit signed integer
|
||||
static CodeFieldSigned<16, 31> simm16;
|
||||
|
||||
//This immediate field is used to specify a 16-bit unsigned integer
|
||||
static CodeField<16, 31> uimm16;
|
||||
|
||||
static CodeField<6, 31> uimm26;
|
||||
|
||||
/*
|
||||
Record bit.
|
||||
0 Does not update the condition register (CR).
|
||||
1 Updates the CR to reflect the result of the operation.
|
||||
For integer instructions, CR bits [0-2] are set to reflect the result as a signed quantity and CR bit [3]
|
||||
receives a copy of the summary overflow bit, XER[SO]. The result as an unsigned quantity or a bit
|
||||
string can be deduced from the EQ bit. For floating-point instructions, CR bits [4-7] are set to reflect
|
||||
floating-point exception, floating-point enabled exception, floating-point invalid operation exception,
|
||||
and floating-point overflow exception.
|
||||
*/
|
||||
static CodeField<31> RC;
|
||||
|
||||
//Primary opcode field
|
||||
static CodeField<0, 5> OPCD;
|
||||
|
||||
static CodeField<26, 31> GD_04; //0x3f
|
||||
static CodeField<21, 31> GD_04_0;//0x7ff
|
||||
static CodeField<21, 30> GD_13; //0x3ff
|
||||
static CodeField<27, 29> GD_1e; //0x7
|
||||
static CodeField<21, 30> GD_1f; //0x3ff
|
||||
static CodeField<30, 31> GD_3a; //0x3
|
||||
static CodeField<26, 30> GD_3b; //0x1f
|
||||
static CodeField<30, 31> GD_3e; //0x3
|
||||
static CodeField<26, 30> GD_3f;//0x1f
|
||||
static CodeField<21, 30> GD_3f_0; //0x3ff
|
||||
|
||||
static CodeField<9, 10> STRM;
|
||||
}
|
||||
|
||||
namespace lists
|
||||
{
|
||||
using namespace fields;
|
||||
|
||||
//static auto main_list = new_list(OPCD, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, OPCD));
|
||||
static InstrList<1 << CodeField<0, 5>::size, ::PPUOpcodes> main_list_obj(OPCD, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, OPCD));
|
||||
static auto main_list = &main_list_obj;
|
||||
static auto g04_list = new_list(main_list, PPU_opcodes::G_04, GD_04);
|
||||
static auto g04_0_list = new_list(g04_list, GD_04_0, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_04_0));
|
||||
static auto g13_list = new_list(main_list, PPU_opcodes::G_13, GD_13, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_13));
|
||||
static auto g1e_list = new_list(main_list, PPU_opcodes::G_1e, GD_1e, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_1e));
|
||||
static auto g1f_list = new_list(main_list, PPU_opcodes::G_1f, GD_1f, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_1f));
|
||||
static auto g3a_list = new_list(main_list, PPU_opcodes::G_3a, GD_3a, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_3a));
|
||||
static auto g3b_list = new_list(main_list, PPU_opcodes::G_3b, GD_3b, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_3b));
|
||||
static auto g3e_list = new_list(main_list, PPU_opcodes::G_3e, GD_3e, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_3e));
|
||||
static auto g3f_list = new_list(main_list, PPU_opcodes::G_3f, GD_3f);
|
||||
static auto g3f_0_list = new_list(g3f_list, GD_3f_0, instr_bind(&PPUOpcodes::UNK, GetCode, OPCD, GD_3f_0));
|
||||
|
||||
#define bind_instr(list, name, ...) \
|
||||
static const auto& name = make_instr<PPU_opcodes::name>(list, #name, &PPUOpcodes::name, ##__VA_ARGS__)
|
||||
#define bind_instr_oe(list, name, ...) \
|
||||
bind_instr(list, name, ##__VA_ARGS__); \
|
||||
static const auto& name##O = make_instr<PPU_opcodes::name##O>(list, #name "O", &PPUOpcodes::name, ##__VA_ARGS__)
|
||||
|
||||
bind_instr(main_list, TDI, TO, RA, simm16);
|
||||
bind_instr(main_list, TWI, TO, RA, simm16);
|
||||
bind_instr(main_list, MULLI, RD, RA, simm16);
|
||||
bind_instr(main_list, SUBFIC, RD, RA, simm16);
|
||||
bind_instr(main_list, CMPLI, CRFD, L_10, RA, uimm16);
|
||||
bind_instr(main_list, CMPI, CRFD, L_10, RA, simm16);
|
||||
bind_instr(main_list, ADDIC, RD, RA, simm16);
|
||||
bind_instr(main_list, ADDIC_, RD, RA, simm16);
|
||||
bind_instr(main_list, ADDI, RD, RA, simm16);
|
||||
bind_instr(main_list, ADDIS, RD, RA, simm16);
|
||||
bind_instr(main_list, BC, BO, BI, BD, AA, LK);
|
||||
bind_instr(main_list, HACK, uimm26);
|
||||
bind_instr(main_list, SC, LEV);
|
||||
bind_instr(main_list, B, LL, AA, LK);
|
||||
bind_instr(main_list, RLWIMI, RA, RS, SH, MB, ME, RC);
|
||||
bind_instr(main_list, RLWINM, RA, RS, SH, MB, ME, RC);
|
||||
bind_instr(main_list, RLWNM, RA, RS, RB, MB, ME, RC);
|
||||
bind_instr(main_list, ORI, RA, RS, uimm16);
|
||||
bind_instr(main_list, ORIS, RA, RS, uimm16);
|
||||
bind_instr(main_list, XORI, RA, RS, uimm16);
|
||||
bind_instr(main_list, XORIS, RA, RS, uimm16);
|
||||
bind_instr(main_list, ANDI_, RA, RS, uimm16);
|
||||
bind_instr(main_list, ANDIS_, RA, RS, uimm16);
|
||||
bind_instr(main_list, LWZ, RD, RA, D);
|
||||
bind_instr(main_list, LWZU, RD, RA, D);
|
||||
bind_instr(main_list, LBZ, RD, RA, D);
|
||||
bind_instr(main_list, LBZU, RD, RA, D);
|
||||
bind_instr(main_list, STW, RS, RA, D);
|
||||
bind_instr(main_list, STWU, RS, RA, D);
|
||||
bind_instr(main_list, STB, RS, RA, D);
|
||||
bind_instr(main_list, STBU, RS, RA, D);
|
||||
bind_instr(main_list, LHZ, RD, RA, D);
|
||||
bind_instr(main_list, LHZU, RD, RA, D);
|
||||
bind_instr(main_list, LHA, RD, RA, D);
|
||||
bind_instr(main_list, LHAU, RD, RA, D);
|
||||
bind_instr(main_list, STH, RS, RA, D);
|
||||
bind_instr(main_list, STHU, RS, RA, D);
|
||||
bind_instr(main_list, LMW, RD, RA, D);
|
||||
bind_instr(main_list, STMW, RS, RA, D);
|
||||
bind_instr(main_list, LFS, FRD, RA, D);
|
||||
bind_instr(main_list, LFSU, FRD, RA, D);
|
||||
bind_instr(main_list, LFD, FRD, RA, D);
|
||||
bind_instr(main_list, LFDU, FRD, RA, D);
|
||||
bind_instr(main_list, STFS, FRS, RA, D);
|
||||
bind_instr(main_list, STFSU, FRS, RA, D);
|
||||
bind_instr(main_list, STFD, FRS, RA, D);
|
||||
bind_instr(main_list, STFDU, FRS, RA, D);
|
||||
|
||||
bind_instr(g04_list, VMADDFP, VD, VA, VC, VB);
|
||||
bind_instr(g04_list, VMHADDSHS, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMHRADDSHS, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMLADDUHM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMMBM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMSHM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMSHS, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMUBM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMUHM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VMSUMUHS, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VNMSUBFP, VD, VA, VC, VB);
|
||||
bind_instr(g04_list, VPERM, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VSEL, VD, VA, VB, VC);
|
||||
bind_instr(g04_list, VSLDOI, VD, VA, VB, VSH);
|
||||
|
||||
bind_instr(g04_0_list, MFVSCR, VD);
|
||||
bind_instr(g04_0_list, MTVSCR, VB);
|
||||
bind_instr(g04_0_list, VADDCUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDSBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDSHS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDSWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUBM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUHM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUHS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUWM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VADDUWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAND, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VANDC, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGSB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGSH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGSW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VAVGUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCFSX, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VCFUX, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VCMPBFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPBFP_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQFP_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUB_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUH_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPEQUW_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGEFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGEFP_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTFP_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSB_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSH_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTSW_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUB_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUH_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCMPGTUW_, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VCTSXS, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VCTUXS, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VEXPTEFP, VD, VB);
|
||||
bind_instr(g04_0_list, VLOGEFP, VD, VB);
|
||||
bind_instr(g04_0_list, VMAXFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXSB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXSH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXSW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMAXUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINSB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINSH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINSW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMINUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGHB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGHH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGHW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGLB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGLH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMRGLW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULESB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULESH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULEUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULEUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULOSB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULOSH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULOUB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VMULOUH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VNOR, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VOR, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKPX, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKSHSS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKSHUS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKSWSS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKSWUS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKUHUM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKUHUS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKUWUM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VPKUWUS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VREFP, VD, VB);
|
||||
bind_instr(g04_0_list, VRFIM, VD, VB);
|
||||
bind_instr(g04_0_list, VRFIN, VD, VB);
|
||||
bind_instr(g04_0_list, VRFIP, VD, VB);
|
||||
bind_instr(g04_0_list, VRFIZ, VD, VB);
|
||||
bind_instr(g04_0_list, VRLB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VRLH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VRLW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VRSQRTEFP, VD, VB);
|
||||
bind_instr(g04_0_list, VSL, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSLB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSLH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSLO, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSLW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSPLTB, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VSPLTH, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VSPLTISB, VD, VSIMM);
|
||||
bind_instr(g04_0_list, VSPLTISH, VD, VSIMM);
|
||||
bind_instr(g04_0_list, VSPLTISW, VD, VSIMM);
|
||||
bind_instr(g04_0_list, VSPLTW, VD, VUIMM, VB);
|
||||
bind_instr(g04_0_list, VSR, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRAB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRAH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRAW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRB, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRH, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRO, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSRW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBCUW, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBFP, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBSBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBSHS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBSWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUBM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUHM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUHS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUWM, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUBUWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUMSWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUM2SWS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUM4SBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUM4SHS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VSUM4UBS, VD, VA, VB);
|
||||
bind_instr(g04_0_list, VUPKHPX, VD, VB);
|
||||
bind_instr(g04_0_list, VUPKHSB, VD, VB);
|
||||
bind_instr(g04_0_list, VUPKHSH, VD, VB);
|
||||
bind_instr(g04_0_list, VUPKLPX, VD, VB);
|
||||
bind_instr(g04_0_list, VUPKLSB, VD, VB);
|
||||
bind_instr(g04_0_list, VUPKLSH, VD, VB);
|
||||
bind_instr(g04_0_list, VXOR, VD, VA, VB);
|
||||
|
||||
bind_instr(g13_list, MCRF, CRFD, CRFS);
|
||||
bind_instr(g13_list, BCLR, BO, BI, BH, LK);
|
||||
bind_instr(g13_list, CRNOR, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CRANDC, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, ISYNC);
|
||||
bind_instr(g13_list, CRXOR, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CRNAND, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CRAND, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CREQV, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CRORC, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, CROR, CRBD, CRBA, CRBB);
|
||||
bind_instr(g13_list, BCCTR, BO, BI, BH, LK);
|
||||
|
||||
bind_instr(g1e_list, RLDICL, RA, RS, sh, mb, RC);
|
||||
bind_instr(g1e_list, RLDICR, RA, RS, sh, me, RC);
|
||||
bind_instr(g1e_list, RLDIC, RA, RS, sh, mb, RC);
|
||||
bind_instr(g1e_list, RLDIMI, RA, RS, sh, mb, RC);
|
||||
bind_instr(g1e_list, RLDC_LR, RA, RS, RB, mb, AA, RC);
|
||||
|
||||
/*0x000*/bind_instr(g1f_list, CMP, CRFD, L_10, RA, RB);
|
||||
/*0x004*/bind_instr(g1f_list, TW, TO, RA, RB);
|
||||
/*0x006*/bind_instr(g1f_list, LVSL, VD, RA, RB);
|
||||
/*0x007*/bind_instr(g1f_list, LVEBX, VD, RA, RB);
|
||||
/*0x008*/bind_instr_oe(g1f_list, SUBFC, RD, RA, RB, OE, RC);
|
||||
/*0x009*/bind_instr(g1f_list, MULHDU, RD, RA, RB, RC);
|
||||
/*0x00a*/bind_instr_oe(g1f_list, ADDC, RD, RA, RB, OE, RC);
|
||||
/*0x00b*/bind_instr(g1f_list, MULHWU, RD, RA, RB, RC);
|
||||
/*0x013*/bind_instr(g1f_list, MFOCRF, L_11, RD, CRM);
|
||||
/*0x014*/bind_instr(g1f_list, LWARX, RD, RA, RB);
|
||||
/*0x015*/bind_instr(g1f_list, LDX, RD, RA, RB);
|
||||
/*0x017*/bind_instr(g1f_list, LWZX, RD, RA, RB);
|
||||
/*0x018*/bind_instr(g1f_list, SLW, RA, RS, RB, RC);
|
||||
/*0x01a*/bind_instr(g1f_list, CNTLZW, RA, RS, RC);
|
||||
/*0x01b*/bind_instr(g1f_list, SLD, RA, RS, RB, RC);
|
||||
/*0x01c*/bind_instr(g1f_list, AND, RA, RS, RB, RC);
|
||||
/*0x020*/bind_instr(g1f_list, CMPL, CRFD, L_10, RA, RB);
|
||||
/*0x026*/bind_instr(g1f_list, LVSR, VD, RA, RB);
|
||||
/*0x027*/bind_instr(g1f_list, LVEHX, VD, RA, RB);
|
||||
/*0x028*/bind_instr_oe(g1f_list, SUBF, RD, RA, RB, OE, RC);
|
||||
/*0x035*/bind_instr(g1f_list, LDUX, RD, RA, RB);
|
||||
/*0x036*/bind_instr(g1f_list, DCBST, RA, RB);
|
||||
/*0x037*/bind_instr(g1f_list, LWZUX, RD, RA, RB);
|
||||
/*0x03a*/bind_instr(g1f_list, CNTLZD, RA, RS, RC);
|
||||
/*0x03c*/bind_instr(g1f_list, ANDC, RA, RS, RB, RC);
|
||||
/*0x03c*/bind_instr(g1f_list, TD, TO, RA, RB);
|
||||
/*0x047*/bind_instr(g1f_list, LVEWX, VD, RA, RB);
|
||||
/*0x049*/bind_instr(g1f_list, MULHD, RD, RA, RB, RC);
|
||||
/*0x04b*/bind_instr(g1f_list, MULHW, RD, RA, RB, RC);
|
||||
/*0x054*/bind_instr(g1f_list, LDARX, RD, RA, RB);
|
||||
/*0x056*/bind_instr(g1f_list, DCBF, RA, RB);
|
||||
/*0x057*/bind_instr(g1f_list, LBZX, RD, RA, RB);
|
||||
/*0x067*/bind_instr(g1f_list, LVX, VD, RA, RB);
|
||||
/*0x068*/bind_instr_oe(g1f_list, NEG, RD, RA, OE, RC);
|
||||
/*0x077*/bind_instr(g1f_list, LBZUX, RD, RA, RB);
|
||||
/*0x07c*/bind_instr(g1f_list, NOR, RA, RS, RB, RC);
|
||||
/*0x087*/bind_instr(g1f_list, STVEBX, VS, RA, RB);
|
||||
/*0x088*/bind_instr_oe(g1f_list, SUBFE, RD, RA, RB, OE, RC);
|
||||
/*0x08a*/bind_instr_oe(g1f_list, ADDE, RD, RA, RB, OE, RC);
|
||||
/*0x090*/bind_instr(g1f_list, MTOCRF, L_11, CRM, RS);
|
||||
/*0x095*/bind_instr(g1f_list, STDX, RS, RA, RB);
|
||||
/*0x096*/bind_instr(g1f_list, STWCX_, RS, RA, RB);
|
||||
/*0x097*/bind_instr(g1f_list, STWX, RS, RA, RB);
|
||||
/*0x0a7*/bind_instr(g1f_list, STVEHX, VS, RA, RB);
|
||||
/*0x0b5*/bind_instr(g1f_list, STDUX, RS, RA, RB);
|
||||
/*0x0b7*/bind_instr(g1f_list, STWUX, RS, RA, RB);
|
||||
/*0x0c7*/bind_instr(g1f_list, STVEWX, VS, RA, RB);
|
||||
/*0x0c8*/bind_instr_oe(g1f_list, SUBFZE, RD, RA, OE, RC);
|
||||
/*0x0ca*/bind_instr_oe(g1f_list, ADDZE, RD, RA, OE, RC);
|
||||
/*0x0d6*/bind_instr(g1f_list, STDCX_, RS, RA, RB);
|
||||
/*0x0d7*/bind_instr(g1f_list, STBX, RS, RA, RB);
|
||||
/*0x0e7*/bind_instr(g1f_list, STVX, VS, RA, RB);
|
||||
/*0x0e8*/bind_instr_oe(g1f_list, SUBFME, RD, RA, OE, RC);
|
||||
/*0x0e9*/bind_instr_oe(g1f_list, MULLD, RD, RA, RB, OE, RC);
|
||||
/*0x0ea*/bind_instr_oe(g1f_list, ADDME, RD, RA, OE, RC);
|
||||
/*0x0eb*/bind_instr_oe(g1f_list, MULLW, RD, RA, RB, OE, RC);
|
||||
/*0x0f6*/bind_instr(g1f_list, DCBTST, RA, RB, TH);
|
||||
/*0x0f7*/bind_instr(g1f_list, STBUX, RS, RA, RB);
|
||||
/*0x10a*/bind_instr_oe(g1f_list, ADD, RD, RA, RB, OE, RC);
|
||||
/*0x116*/bind_instr(g1f_list, DCBT, RA, RB, TH);
|
||||
/*0x117*/bind_instr(g1f_list, LHZX, RD, RA, RB);
|
||||
/*0x11c*/bind_instr(g1f_list, EQV, RA, RS, RB, RC);
|
||||
/*0x136*/bind_instr(g1f_list, ECIWX, RD, RA, RB);
|
||||
/*0x137*/bind_instr(g1f_list, LHZUX, RD, RA, RB);
|
||||
/*0x13c*/bind_instr(g1f_list, XOR, RA, RS, RB, RC);
|
||||
/*0x153*/bind_instr(g1f_list, MFSPR, RD, SPR);
|
||||
/*0x155*/bind_instr(g1f_list, LWAX, RD, RA, RB);
|
||||
/*0x156*/bind_instr(g1f_list, DST, RA, RB, STRM, L_6);
|
||||
/*0x157*/bind_instr(g1f_list, LHAX, RD, RA, RB);
|
||||
/*0x167*/bind_instr(g1f_list, LVXL, VD, RA, RB);
|
||||
/*0x173*/bind_instr(g1f_list, MFTB, RD, SPR);
|
||||
/*0x175*/bind_instr(g1f_list, LWAUX, RD, RA, RB);
|
||||
/*0x176*/bind_instr(g1f_list, DSTST, RA, RB, STRM, L_6);
|
||||
/*0x177*/bind_instr(g1f_list, LHAUX, RD, RA, RB);
|
||||
/*0x197*/bind_instr(g1f_list, STHX, RS, RA, RB);
|
||||
/*0x19c*/bind_instr(g1f_list, ORC, RA, RS, RB, RC);
|
||||
/*0x1b6*/bind_instr(g1f_list, ECOWX, RS, RA, RB);
|
||||
/*0x1b7*/bind_instr(g1f_list, STHUX, RS, RA, RB);
|
||||
/*0x1bc*/bind_instr(g1f_list, OR, RA, RS, RB, RC);
|
||||
/*0x1c9*/bind_instr_oe(g1f_list, DIVDU, RD, RA, RB, OE, RC);
|
||||
/*0x1cb*/bind_instr_oe(g1f_list, DIVWU, RD, RA, RB, OE, RC);
|
||||
/*0x1d3*/bind_instr(g1f_list, MTSPR, SPR, RS);
|
||||
/*0x1d6*///DCBI
|
||||
/*0x1dc*/bind_instr(g1f_list, NAND, RA, RS, RB, RC);
|
||||
/*0x1e7*/bind_instr(g1f_list, STVXL, VS, RA, RB);
|
||||
/*0x1e9*/bind_instr_oe(g1f_list, DIVD, RD, RA, RB, OE, RC);
|
||||
/*0x1eb*/bind_instr_oe(g1f_list, DIVW, RD, RA, RB, OE, RC);
|
||||
/*0x207*/bind_instr(g1f_list, LVLX, VD, RA, RB);
|
||||
// MULH{D|DU|W|WU} don't use OE, but a real Cell accepts
|
||||
// opcodes with OE=1 and Rc=0, behaving as if OE was not set.
|
||||
// OE=1 and Rc=1 causes an invalid instruction exception, but
|
||||
// we don't worry about that.
|
||||
static const auto& MULHDUO = make_instr<0x209>(g1f_list, "MULHDUO", &PPUOpcodes::MULHDU, RD, RA, RB, RC);
|
||||
static const auto& MULHWUO = make_instr<0x20b>(g1f_list, "MULHWUO", &PPUOpcodes::MULHWU, RD, RA, RB, RC);
|
||||
/*0x214*/bind_instr(g1f_list, LDBRX, RD, RA, RB);
|
||||
/*0x215*/bind_instr(g1f_list, LSWX, RD, RA, RB);
|
||||
/*0x216*/bind_instr(g1f_list, LWBRX, RD, RA, RB);
|
||||
/*0x217*/bind_instr(g1f_list, LFSX, FRD, RA, RB);
|
||||
/*0x218*/bind_instr(g1f_list, SRW, RA, RS, RB, RC);
|
||||
/*0x21b*/bind_instr(g1f_list, SRD, RA, RS, RB, RC);
|
||||
/*0x227*/bind_instr(g1f_list, LVRX, VD, RA, RB);
|
||||
/*0x237*/bind_instr(g1f_list, LFSUX, FRD, RA, RB);
|
||||
static const auto& MULHDO = make_instr<0x249>(g1f_list, "MULHDO", &PPUOpcodes::MULHD, RD, RA, RB, RC);
|
||||
static const auto& MULHWO = make_instr<0x24b>(g1f_list, "MULHWO", &PPUOpcodes::MULHW, RD, RA, RB, RC);
|
||||
/*0x255*/bind_instr(g1f_list, LSWI, RD, RA, NB);
|
||||
/*0x256*/bind_instr(g1f_list, SYNC, L_9_10);
|
||||
/*0x257*/bind_instr(g1f_list, LFDX, FRD, RA, RB);
|
||||
/*0x277*/bind_instr(g1f_list, LFDUX, FRD, RA, RB);
|
||||
/*0x287*/bind_instr(g1f_list, STVLX, VS, RA, RB);
|
||||
/*0x294*/bind_instr(g1f_list, STDBRX, RD, RA, RB);
|
||||
/*0x296*/bind_instr(g1f_list, STSWX, RS, RA, RB);
|
||||
/*0x296*/bind_instr(g1f_list, STWBRX, RS, RA, RB);
|
||||
/*0x297*/bind_instr(g1f_list, STFSX, FRS, RA, RB);
|
||||
/*0x2a7*/bind_instr(g1f_list, STVRX, VS, RA, RB);
|
||||
/*0x2b7*/bind_instr(g1f_list, STFSUX, FRS, RA, RB);
|
||||
/*0x2d5*/bind_instr(g1f_list, STSWI, RS, RA, NB);
|
||||
/*0x2d7*/bind_instr(g1f_list, STFDX, FRS, RA, RB);
|
||||
/*0x2d7*/bind_instr(g1f_list, STFDUX, FRS, RA, RB);
|
||||
/*0x307*/bind_instr(g1f_list, LVLXL, VD, RA, RB);
|
||||
/*0x316*/bind_instr(g1f_list, LHBRX, RD, RA, RB);
|
||||
/*0x318*/bind_instr(g1f_list, SRAW, RA, RS, RB, RC);
|
||||
/*0x31a*/bind_instr(g1f_list, SRAD, RA, RS, RB, RC);
|
||||
/*0x327*/bind_instr(g1f_list, LVRXL, VD, RA, RB);
|
||||
/*0x336*/bind_instr(g1f_list, DSS, STRM, L_6);
|
||||
/*0x338*/bind_instr(g1f_list, SRAWI, RA, RS, SH, RC);
|
||||
/*0x33a*/bind_instr(g1f_list, SRADI1, RA, RS, sh, RC);
|
||||
/*0x33b*/bind_instr(g1f_list, SRADI2, RA, RS, sh, RC);
|
||||
/*0x356*/bind_instr(g1f_list, EIEIO);
|
||||
/*0x387*/bind_instr(g1f_list, STVLXL, VS, RA, RB);
|
||||
/*0x396*/bind_instr(g1f_list, STHBRX, RS, RA, RB);
|
||||
/*0x39a*/bind_instr(g1f_list, EXTSH, RA, RS, RC);
|
||||
/*0x387*/bind_instr(g1f_list, STVRXL, VS, RA, RB);
|
||||
/*0x3ba*/bind_instr(g1f_list, EXTSB, RA, RS, RC);
|
||||
/*0x3d7*/bind_instr(g1f_list, STFIWX, FRS, RA, RB);
|
||||
/*0x3da*/bind_instr(g1f_list, EXTSW, RA, RS, RC);
|
||||
/*0x3d6*/bind_instr(g1f_list, ICBI, RA, RB);
|
||||
/*0x3f6*/bind_instr(g1f_list, DCBZ, RA, RB);
|
||||
|
||||
bind_instr(g3a_list, LD, RD, RA, DS);
|
||||
bind_instr(g3a_list, LDU, RD, RA, DS);
|
||||
bind_instr(g3a_list, LWA, RD, RA, DS);
|
||||
|
||||
bind_instr(g3b_list, FDIVS, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3b_list, FSUBS, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3b_list, FADDS, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3b_list, FSQRTS, FRD, FRB, RC);
|
||||
bind_instr(g3b_list, FRES, FRD, FRB, RC);
|
||||
bind_instr(g3b_list, FMULS, FRD, FRA, FRC, RC);
|
||||
bind_instr(g3b_list, FMADDS, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3b_list, FMSUBS, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3b_list, FNMSUBS, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3b_list, FNMADDS, FRD, FRA, FRC, FRB, RC);
|
||||
|
||||
bind_instr(g3e_list, STD, RS, RA, DS);
|
||||
bind_instr(g3e_list, STDU, RS, RA, DS);
|
||||
|
||||
bind_instr(g3f_list, FSEL, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3f_list, FMUL, FRD, FRA, FRC, RC);
|
||||
bind_instr(g3f_list, FMSUB, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3f_list, FMADD, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3f_list, FNMSUB, FRD, FRA, FRC, FRB, RC);
|
||||
bind_instr(g3f_list, FNMADD, FRD, FRA, FRC, FRB, RC);
|
||||
|
||||
bind_instr(g3f_0_list, FDIV, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3f_0_list, FSUB, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3f_0_list, FADD, FRD, FRA, FRB, RC);
|
||||
bind_instr(g3f_0_list, FSQRT, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FRSQRTE, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCMPU, CRFD, FRA, FRB);
|
||||
bind_instr(g3f_0_list, FRSP, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCTIW, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCTIWZ, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCMPO, CRFD, FRA, FRB);
|
||||
bind_instr(g3f_0_list, FNEG, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FMR, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FNABS, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FABS, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCFID, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCTID, FRD, FRB, RC);
|
||||
bind_instr(g3f_0_list, FCTIDZ, FRD, FRB, RC);
|
||||
|
||||
bind_instr(g3f_0_list, MTFSB1, CRBD, RC);
|
||||
bind_instr(g3f_0_list, MCRFS, CRFD, CRFS);
|
||||
bind_instr(g3f_0_list, MTFSB0, CRBD, RC);
|
||||
bind_instr(g3f_0_list, MTFSFI, CRFD, I, RC);
|
||||
bind_instr(g3f_0_list, MFFS, FRD, RC);
|
||||
bind_instr(g3f_0_list, MTFSF, FM, FRB, RC);
|
||||
|
||||
enum
|
||||
{
|
||||
r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11,
|
||||
r12, r13, r14, r15, r16, r17, r18, r19, r20, r21,
|
||||
r22, r23, r24, r25, r26, r27, r28, r29, r30, r31
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
cr0, cr1, cr2, cr3, cr4, cr5, cr6, cr7
|
||||
};
|
||||
}
|
||||
|
||||
namespace implicts
|
||||
{
|
||||
using namespace lists;
|
||||
|
||||
inline u32 LIS(u32 reg, u32 imm) { return ADDIS(reg, r0, imm); }
|
||||
inline u32 LI_(u32 reg, u32 imm) { return ADDI(reg, r0, imm); }
|
||||
inline u32 NOP() { return ORI(r0, r0, 0); }
|
||||
inline u32 MR(u32 x, u32 y) { return OR(x, y, y, false); }
|
||||
inline u32 BLR() { return BCLR(0x10 | 0x04, 0, 0, 0); }
|
||||
inline u32 BCTR() { return BCCTR(0x10 | 0x04, 0, 0, 0); }
|
||||
inline u32 BCTRL() { return BCCTR(0x10 | 0x04, 0, 0, 1); }
|
||||
inline u32 MFCTR(u32 reg) { return MFSPR(reg, 9 << 5); }
|
||||
inline u32 MTCTR(u32 reg) { return MTSPR(9 << 5, reg); }
|
||||
inline u32 MFLR(u32 reg) { return MFSPR(reg, 8 << 5); }
|
||||
inline u32 MTLR(u32 reg) { return MTSPR(8 << 5, reg); }
|
||||
|
||||
inline u32 BNE(u32 cr, s32 imm) { return BC(4, 2 | cr << 2, imm, 0, 0); }
|
||||
inline u32 BEQ(u32 cr, s32 imm) { return BC(12, 2 | cr << 2, imm, 0, 0); }
|
||||
inline u32 BGT(u32 cr, s32 imm) { return BC(12, 1 | cr << 2, imm, 0, 0); }
|
||||
|
||||
inline u32 BNE(s32 imm) { return BNE(cr0, imm); }
|
||||
inline u32 BEQ(s32 imm) { return BEQ(cr0, imm); }
|
||||
inline u32 BGT(s32 imm) { return BGT(cr0, imm); }
|
||||
|
||||
inline u32 CMPDI(u32 cr, u32 reg, u32 imm) { return CMPI(cr, 1, reg, imm); }
|
||||
inline u32 CMPDI(u32 reg, u32 imm) { return CMPDI(cr0, reg, imm); }
|
||||
|
||||
inline u32 CMPWI(u32 cr, u32 reg, u32 imm) { return CMPI(cr, 0, reg, imm); }
|
||||
inline u32 CMPWI(u32 reg, u32 imm) { return CMPWI(cr0, reg, imm); }
|
||||
|
||||
inline u32 CMPLDI(u32 cr, u32 reg, u32 imm) { return CMPLI(cr, 1, reg, imm); }
|
||||
inline u32 CMPLDI(u32 reg, u32 imm) { return CMPLDI(cr0, reg, imm); }
|
||||
|
||||
inline u32 CMPLWI(u32 cr, u32 reg, u32 imm) { return CMPLI(cr, 0, reg, imm); }
|
||||
inline u32 CMPLWI(u32 reg, u32 imm) { return CMPLWI(cr0, reg, imm); }
|
||||
|
||||
inline u32 EXTRDI(u32 x, u32 y, u32 n, u32 b) { return RLDICL(x, y, b + n, 64 - b, false); }
|
||||
inline u32 SRDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 64 - n, n, false); }
|
||||
inline u32 CLRLDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 0, n, false); }
|
||||
}
|
||||
|
||||
using namespace lists;
|
||||
using namespace implicts;
|
||||
#undef bind_instr
|
||||
};
|
@ -1,680 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#ifdef LLVM_AVAILABLE
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/state.h"
|
||||
#include "Emu/Cell/PPUDisAsm.h"
|
||||
#include "Emu/Cell/PPULLVMRecompiler.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Utilities/VirtualMemory.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/ExecutionEngine/GenericValue.h"
|
||||
#include "llvm/ExecutionEngine/MCJIT.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Analysis/Passes.h"
|
||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Vectorize.h"
|
||||
#include "llvm/MC/MCDisassembler.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
using namespace ppu_recompiler_llvm;
|
||||
|
||||
#ifdef ID_MANAGER_INCLUDED
|
||||
#error "ID Manager cannot be used in this module"
|
||||
#endif
|
||||
|
||||
// PS3 can address 32 bits aligned on 4 bytes boundaries : 2^30 pointers
|
||||
#define VIRTUAL_INSTRUCTION_COUNT 0x40000000
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
u64 Compiler::s_rotate_mask[64][64];
|
||||
bool Compiler::s_rotate_mask_inited = false;
|
||||
|
||||
std::unique_ptr<Module> Compiler::create_module(LLVMContext &llvm_context)
|
||||
{
|
||||
const std::vector<Type *> arg_types = { Type::getInt8PtrTy(llvm_context), Type::getInt64Ty(llvm_context) };
|
||||
FunctionType *compiled_function_type = FunctionType::get(Type::getInt32Ty(llvm_context), arg_types, false);
|
||||
|
||||
std::unique_ptr<llvm::Module> result(new llvm::Module("Module", llvm_context));
|
||||
Function *execute_unknown_function = (Function *)result->getOrInsertFunction("execute_unknown_function", compiled_function_type);
|
||||
execute_unknown_function->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
Function *execute_unknown_block = (Function *)result->getOrInsertFunction("execute_unknown_block", compiled_function_type);
|
||||
execute_unknown_block->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
std::string targetTriple = "x86_64-pc-windows-elf";
|
||||
result->setTargetTriple(targetTriple);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Compiler::optimise_module(llvm::Module *module)
|
||||
{
|
||||
llvm::FunctionPassManager fpm(module);
|
||||
fpm.add(createNoAAPass());
|
||||
fpm.add(createBasicAliasAnalysisPass());
|
||||
fpm.add(createNoTargetTransformInfoPass());
|
||||
fpm.add(createEarlyCSEPass());
|
||||
fpm.add(createTailCallEliminationPass());
|
||||
fpm.add(createReassociatePass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(new DominatorTreeWrapperPass());
|
||||
fpm.add(new MemoryDependenceAnalysis());
|
||||
fpm.add(createGVNPass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(new MemoryDependenceAnalysis());
|
||||
fpm.add(createDeadStoreEliminationPass());
|
||||
fpm.add(new LoopInfo());
|
||||
fpm.add(new ScalarEvolution());
|
||||
fpm.add(createSLPVectorizerPass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(createCFGSimplificationPass());
|
||||
fpm.doInitialization();
|
||||
|
||||
for (auto I = module->begin(), E = module->end(); I != E; ++I)
|
||||
fpm.run(*I);
|
||||
}
|
||||
|
||||
|
||||
Compiler::Compiler(LLVMContext *context, llvm::IRBuilder<> *builder, std::unordered_map<std::string, void*> &function_ptrs)
|
||||
: m_llvm_context(context),
|
||||
m_ir_builder(builder),
|
||||
m_executable_map(function_ptrs) {
|
||||
|
||||
std::vector<Type *> arg_types;
|
||||
arg_types.push_back(m_ir_builder->getInt8PtrTy());
|
||||
arg_types.push_back(m_ir_builder->getInt64Ty());
|
||||
m_compiled_function_type = FunctionType::get(m_ir_builder->getInt32Ty(), arg_types, false);
|
||||
|
||||
if (!s_rotate_mask_inited) {
|
||||
InitRotateMask();
|
||||
s_rotate_mask_inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
Compiler::~Compiler() {
|
||||
}
|
||||
|
||||
void Compiler::initiate_function(const std::string &name)
|
||||
{
|
||||
m_state.function = (Function *)m_module->getOrInsertFunction(name, m_compiled_function_type);
|
||||
m_state.function->setCallingConv(CallingConv::X86_64_Win64);
|
||||
auto arg_i = m_state.function->arg_begin();
|
||||
arg_i->setName("ppu_state");
|
||||
m_state.args[CompileTaskState::Args::State] = arg_i;
|
||||
(++arg_i)->setName("context");
|
||||
m_state.args[CompileTaskState::Args::Context] = arg_i;
|
||||
}
|
||||
|
||||
void ppu_recompiler_llvm::Compiler::translate_to_llvm_ir(llvm::Module *module, const std::string & name, u32 start_address, u32 instruction_count)
|
||||
{
|
||||
m_module = module;
|
||||
|
||||
m_execute_unknown_function = module->getFunction("execute_unknown_function");
|
||||
m_execute_unknown_block = module->getFunction("execute_unknown_block");
|
||||
|
||||
initiate_function(name);
|
||||
|
||||
// Create the entry block and add code to branch to the first instruction
|
||||
m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0));
|
||||
m_ir_builder->CreateBr(GetBasicBlockFromAddress(start_address));
|
||||
|
||||
// Convert each instruction in the CFG to LLVM IR
|
||||
std::vector<PHINode *> exit_instr_list;
|
||||
for (u32 instructionAddress = start_address; instructionAddress < start_address + instruction_count * 4; instructionAddress += 4) {
|
||||
m_state.hit_branch_instruction = false;
|
||||
m_state.current_instruction_address = instructionAddress;
|
||||
BasicBlock *instr_bb = GetBasicBlockFromAddress(instructionAddress);
|
||||
m_ir_builder->SetInsertPoint(instr_bb);
|
||||
|
||||
u32 instr = vm::ps3::read32(instructionAddress);
|
||||
|
||||
Decode(instr);
|
||||
if (!m_state.hit_branch_instruction)
|
||||
m_ir_builder->CreateBr(GetBasicBlockFromAddress(instructionAddress + 4));
|
||||
}
|
||||
|
||||
// Generate exit logic for all empty blocks
|
||||
const std::string &default_exit_block_name = GetBasicBlockNameFromAddress(0xFFFFFFFF);
|
||||
for (BasicBlock &block_i : *m_state.function) {
|
||||
if (!block_i.getInstList().empty() || block_i.getName() == default_exit_block_name)
|
||||
continue;
|
||||
|
||||
// Found an empty block
|
||||
m_state.current_instruction_address = GetAddressFromBasicBlockName(block_i.getName());
|
||||
|
||||
m_ir_builder->SetInsertPoint(&block_i);
|
||||
PHINode *exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0);
|
||||
exit_instr_list.push_back(exit_instr_i32);
|
||||
|
||||
SetPc(m_ir_builder->getInt32(m_state.current_instruction_address));
|
||||
|
||||
m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusBlockEnded));
|
||||
}
|
||||
|
||||
// If the function has a default exit block then generate code for it
|
||||
BasicBlock *default_exit_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "", false);
|
||||
if (default_exit_bb) {
|
||||
m_ir_builder->SetInsertPoint(default_exit_bb);
|
||||
PHINode *exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0);
|
||||
exit_instr_list.push_back(exit_instr_i32);
|
||||
|
||||
m_ir_builder->CreateRet(m_ir_builder->getInt32(0));
|
||||
}
|
||||
|
||||
// Add incoming values for all exit instr PHI nodes
|
||||
for (PHINode *exit_instr_i : exit_instr_list) {
|
||||
BasicBlock *block = exit_instr_i->getParent();
|
||||
for (pred_iterator pred_i = pred_begin(block); pred_i != pred_end(block); pred_i++) {
|
||||
u32 pred_address = GetAddressFromBasicBlockName((*pred_i)->getName());
|
||||
exit_instr_i->addIncoming(m_ir_builder->getInt32(pred_address), *pred_i);
|
||||
}
|
||||
}
|
||||
|
||||
std::string verify;
|
||||
raw_string_ostream verify_ostream(verify);
|
||||
if (verifyFunction(*m_state.function, &verify_ostream)) {
|
||||
// m_recompilation_engine.trace() << "Verification failed: " << verify_ostream.str() << "\n";
|
||||
}
|
||||
|
||||
m_module = nullptr;
|
||||
m_state.function = nullptr;
|
||||
}
|
||||
|
||||
void Compiler::Decode(const u32 code) {
|
||||
(*PPU_instr::main_list)(this, code);
|
||||
}
|
||||
|
||||
std::mutex RecompilationEngine::s_mutex;
|
||||
std::shared_ptr<RecompilationEngine> RecompilationEngine::s_the_instance = nullptr;
|
||||
|
||||
RecompilationEngine::RecompilationEngine()
|
||||
: m_log(nullptr)
|
||||
, m_currentId(0)
|
||||
, m_last_cache_clear_time(std::chrono::high_resolution_clock::now())
|
||||
, m_llvm_context(getGlobalContext())
|
||||
, m_ir_builder(getGlobalContext()) {
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
InitializeNativeTargetDisassembler();
|
||||
|
||||
FunctionCache = (ExecutableStorageType *)memory_helper::reserve_memory(VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType));
|
||||
// Each char can store 8 page status
|
||||
FunctionCachePagesCommited = (char *)malloc(VIRTUAL_INSTRUCTION_COUNT / (8 * PAGE_SIZE));
|
||||
if (FunctionCachePagesCommited == nullptr)
|
||||
throw EXCEPTION("Memory error");
|
||||
memset(FunctionCachePagesCommited, 0, VIRTUAL_INSTRUCTION_COUNT / (8 * PAGE_SIZE));
|
||||
}
|
||||
|
||||
RecompilationEngine::~RecompilationEngine() {
|
||||
m_executable_storage.clear();
|
||||
memory_helper::free_reserved_memory(FunctionCache, VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType));
|
||||
free(FunctionCachePagesCommited);
|
||||
}
|
||||
|
||||
bool RecompilationEngine::isAddressCommited(u32 address) const
|
||||
{
|
||||
size_t offset = address * sizeof(ExecutableStorageType);
|
||||
size_t page = offset / 4096;
|
||||
// Since bool is stored in char, the char index is page / 8 (or page >> 3)
|
||||
// and we shr the value with the remaining bits (page & 7)
|
||||
return (FunctionCachePagesCommited[page >> 3] >> (page & 7)) & 1;
|
||||
}
|
||||
|
||||
void RecompilationEngine::commitAddress(u32 address)
|
||||
{
|
||||
size_t offset = address * sizeof(ExecutableStorageType);
|
||||
size_t page = offset / 4096;
|
||||
memory_helper::commit_page_memory((u8*)FunctionCache + page * 4096, 4096);
|
||||
// Reverse of isAddressCommited : we set the (page & 7)th bit of (page / 8) th char
|
||||
// in the array
|
||||
FunctionCachePagesCommited[page >> 3] |= (1 << (page & 7));
|
||||
}
|
||||
|
||||
const Executable RecompilationEngine::GetCompiledExecutableIfAvailable(u32 address) const
|
||||
{
|
||||
if (!isAddressCommited(address / 4))
|
||||
return nullptr;
|
||||
u32 id = FunctionCache[address / 4].second;
|
||||
if (rpcs3::state.config.core.llvm.exclusion_range.value() &&
|
||||
(id >= rpcs3::state.config.core.llvm.min_id.value() && id <= rpcs3::state.config.core.llvm.max_id.value()))
|
||||
return nullptr;
|
||||
return FunctionCache[address / 4].first;
|
||||
}
|
||||
|
||||
void RecompilationEngine::NotifyBlockStart(u32 address) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_pending_address_start_lock);
|
||||
if (m_pending_address_start.size() > 10000)
|
||||
m_pending_address_start.clear();
|
||||
m_pending_address_start.push_back(address);
|
||||
}
|
||||
|
||||
if (!is_started()) {
|
||||
start();
|
||||
}
|
||||
|
||||
cv.notify_one();
|
||||
// TODO: Increase the priority of the recompilation engine thread
|
||||
}
|
||||
|
||||
raw_fd_ostream & RecompilationEngine::Log() {
|
||||
if (!m_log) {
|
||||
std::error_code error;
|
||||
m_log = new raw_fd_ostream("PPULLVMRecompiler.log", error, sys::fs::F_Text);
|
||||
m_log->SetUnbuffered();
|
||||
}
|
||||
|
||||
return *m_log;
|
||||
}
|
||||
|
||||
void RecompilationEngine::on_task() {
|
||||
std::chrono::nanoseconds idling_time(0);
|
||||
std::chrono::nanoseconds recompiling_time(0);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
while (!Emu.IsStopped()) {
|
||||
bool work_done_this_iteration = false;
|
||||
std::list <u32> m_current_execution_traces;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_pending_address_start_lock);
|
||||
m_current_execution_traces.swap(m_pending_address_start);
|
||||
}
|
||||
|
||||
if (!m_current_execution_traces.empty()) {
|
||||
for (u32 address : m_current_execution_traces)
|
||||
work_done_this_iteration |= IncreaseHitCounterAndBuild(address);
|
||||
}
|
||||
|
||||
if (!work_done_this_iteration) {
|
||||
// Wait a few ms for something to happen
|
||||
auto idling_start = std::chrono::high_resolution_clock::now();
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait_for(lock, std::chrono::milliseconds(10));
|
||||
auto idling_end = std::chrono::high_resolution_clock::now();
|
||||
idling_time += std::chrono::duration_cast<std::chrono::nanoseconds>(idling_end - idling_start);
|
||||
}
|
||||
}
|
||||
|
||||
s_the_instance = nullptr; // Can cause deadlock if this is the last instance. Need to fix this.
|
||||
}
|
||||
|
||||
bool RecompilationEngine::IncreaseHitCounterAndBuild(u32 address) {
|
||||
auto It = m_block_table.find(address);
|
||||
if (It == m_block_table.end())
|
||||
It = m_block_table.emplace(address, BlockEntry(address)).first;
|
||||
BlockEntry &block = It->second;
|
||||
if (!block.is_compiled) {
|
||||
block.num_hits++;
|
||||
if (block.num_hits >= rpcs3::state.config.core.llvm.threshold.value()) {
|
||||
CompileBlock(block);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern void execute_ppu_func_by_index(PPUThread& ppu, u32 id);
|
||||
extern void execute_syscall_by_index(PPUThread& ppu, u64 code);
|
||||
|
||||
static u32
|
||||
wrappedExecutePPUFuncByIndex(PPUThread &CPU, u32 index) noexcept {
|
||||
try
|
||||
{
|
||||
execute_ppu_func_by_index(CPU, index);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 wrappedDoSyscall(PPUThread &CPU, u64 code) noexcept {
|
||||
try
|
||||
{
|
||||
execute_syscall_by_index(CPU, code);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
static void wrapped_fast_stop(PPUThread &CPU)
|
||||
{
|
||||
CPU.fast_stop();
|
||||
}
|
||||
|
||||
static void wrapped_trap(PPUThread &CPU, u32) noexcept {
|
||||
try
|
||||
{
|
||||
throw EXCEPTION("trap");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Executable, llvm::ExecutionEngine *> RecompilationEngine::compile(const std::string & name, u32 start_address, u32 instruction_count) {
|
||||
std::unique_ptr<llvm::Module> module = Compiler::create_module(m_llvm_context);
|
||||
|
||||
std::unordered_map<std::string, void*> function_ptrs;
|
||||
function_ptrs["execute_unknown_function"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::ExecuteFunction);
|
||||
function_ptrs["execute_unknown_block"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::ExecuteTillReturn);
|
||||
function_ptrs["PollStatus"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::PollStatus);
|
||||
function_ptrs["PPUThread.fast_stop"] = reinterpret_cast<void*>(wrapped_fast_stop);
|
||||
function_ptrs["vm.reservation_acquire"] = reinterpret_cast<void*>(vm::reservation_acquire);
|
||||
function_ptrs["vm.reservation_update"] = reinterpret_cast<void*>(vm::reservation_update);
|
||||
function_ptrs["get_timebased_time"] = reinterpret_cast<void*>(get_timebased_time);
|
||||
function_ptrs["wrappedExecutePPUFuncByIndex"] = reinterpret_cast<void*>(wrappedExecutePPUFuncByIndex);
|
||||
function_ptrs["wrappedDoSyscall"] = reinterpret_cast<void*>(wrappedDoSyscall);
|
||||
function_ptrs["trap"] = reinterpret_cast<void*>(wrapped_trap);
|
||||
|
||||
#define REGISTER_FUNCTION_PTR(name) \
|
||||
function_ptrs[#name] = reinterpret_cast<void*>(PPUInterpreter::name##_impl);
|
||||
|
||||
MACRO_PPU_INST_MAIN_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
MACRO_PPU_INST_G_13_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
MACRO_PPU_INST_G_1E_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
MACRO_PPU_INST_G_1F_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
MACRO_PPU_INST_G_3A_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
MACRO_PPU_INST_G_3E_EXPANDERS(REGISTER_FUNCTION_PTR)
|
||||
|
||||
Compiler(&m_llvm_context, &m_ir_builder, function_ptrs)
|
||||
.translate_to_llvm_ir(module.get(), name, start_address, instruction_count);
|
||||
|
||||
llvm::Module *module_ptr = module.get();
|
||||
|
||||
Log() << *module_ptr;
|
||||
Compiler::optimise_module(module_ptr);
|
||||
|
||||
llvm::ExecutionEngine *execution_engine =
|
||||
EngineBuilder(std::move(module))
|
||||
.setEngineKind(EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::unique_ptr<llvm::SectionMemoryManager>(new CustomSectionMemoryManager(function_ptrs)))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setMCPU("nehalem")
|
||||
.create();
|
||||
module_ptr->setDataLayout(execution_engine->getDataLayout());
|
||||
|
||||
// Translate to machine code
|
||||
execution_engine->finalizeObject();
|
||||
|
||||
Function *llvm_function = module_ptr->getFunction(name);
|
||||
void *function = execution_engine->getPointerToFunction(llvm_function);
|
||||
|
||||
/* m_recompilation_engine.trace() << "\nDisassembly:\n";
|
||||
auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr);
|
||||
for (size_t pc = 0; pc < mci.size();) {
|
||||
char str[1024];
|
||||
|
||||
auto size = LLVMDisasmInstruction(disassembler, ((u8 *)mci.address()) + pc, mci.size() - pc, (uint64_t)(((u8 *)mci.address()) + pc), str, sizeof(str));
|
||||
m_recompilation_engine.trace() << fmt::format("0x%08X: ", (u64)(((u8 *)mci.address()) + pc)) << str << '\n';
|
||||
pc += size;
|
||||
}
|
||||
|
||||
LLVMDisasmDispose(disassembler);*/
|
||||
|
||||
assert(function != nullptr);
|
||||
return std::make_pair((Executable)function, execution_engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is inspired from Dolphin PPC Analyst
|
||||
*/
|
||||
inline s32 SignExt16(s16 x) { return (s32)(s16)x; }
|
||||
inline s32 SignExt26(u32 x) { return x & 0x2000000 ? (s32)(x | 0xFC000000) : (s32)(x); }
|
||||
|
||||
bool RecompilationEngine::AnalyseBlock(BlockEntry &functionData, size_t maxSize)
|
||||
{
|
||||
u32 startAddress = functionData.address;
|
||||
u32 farthestBranchTarget = startAddress;
|
||||
functionData.instructionCount = 0;
|
||||
functionData.calledFunctions.clear();
|
||||
functionData.is_analysed = true;
|
||||
functionData.is_compilable_function = true;
|
||||
Log() << "Analysing " << (void*)(uint64_t)startAddress << "hit " << functionData.num_hits << "\n";
|
||||
// Used to decode instructions
|
||||
PPUDisAsm dis_asm(CPUDisAsm_DumpMode);
|
||||
dis_asm.offset = vm::ps3::_ptr<u8>(startAddress);
|
||||
for (u32 instructionAddress = startAddress; instructionAddress < startAddress + maxSize; instructionAddress += 4)
|
||||
{
|
||||
u32 instr = vm::ps3::read32((u32)instructionAddress);
|
||||
|
||||
dis_asm.dump_pc = instructionAddress - startAddress;
|
||||
(*PPU_instr::main_list)(&dis_asm, instr);
|
||||
Log() << dis_asm.last_opcode;
|
||||
functionData.instructionCount++;
|
||||
if (instr == PPU_instr::implicts::BLR() && instructionAddress >= farthestBranchTarget && functionData.is_compilable_function)
|
||||
{
|
||||
Log() << "Analysis: Block is compilable into a function \n";
|
||||
return true;
|
||||
}
|
||||
else if (PPU_instr::fields::GD_13(instr) == PPU_opcodes::G_13Opcodes::BCCTR)
|
||||
{
|
||||
if (!PPU_instr::fields::LK(instr))
|
||||
{
|
||||
Log() << "Analysis: indirect branching found \n";
|
||||
functionData.is_compilable_function = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (PPU_instr::fields::OPCD(instr) == PPU_opcodes::PPU_MainOpcodes::BC)
|
||||
{
|
||||
u32 target = SignExt16(PPU_instr::fields::BD(instr));
|
||||
if (!PPU_instr::fields::AA(instr)) // Absolute address
|
||||
target += (u32)instructionAddress;
|
||||
if (target > farthestBranchTarget && !PPU_instr::fields::LK(instr))
|
||||
farthestBranchTarget = target;
|
||||
}
|
||||
else if (PPU_instr::fields::OPCD(instr) == PPU_opcodes::PPU_MainOpcodes::B)
|
||||
{
|
||||
u32 target = SignExt26(PPU_instr::fields::LL(instr));
|
||||
if (!PPU_instr::fields::AA(instr)) // Absolute address
|
||||
target += (u32)instructionAddress;
|
||||
|
||||
if (!PPU_instr::fields::LK(instr))
|
||||
{
|
||||
if (target < startAddress)
|
||||
{
|
||||
Log() << "Analysis: branch to previous block\n";
|
||||
functionData.is_compilable_function = false;
|
||||
return true;
|
||||
}
|
||||
else if (target > farthestBranchTarget)
|
||||
farthestBranchTarget = target;
|
||||
}
|
||||
else
|
||||
functionData.calledFunctions.insert(target);
|
||||
}
|
||||
}
|
||||
Log() << "Analysis: maxSize reached \n";
|
||||
functionData.is_compilable_function = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecompilationEngine::CompileBlock(BlockEntry & block_entry) {
|
||||
if (block_entry.is_analysed)
|
||||
return;
|
||||
|
||||
if (!AnalyseBlock(block_entry))
|
||||
return;
|
||||
Log() << "Compile: " << block_entry.ToString() << "\n";
|
||||
|
||||
{
|
||||
// We create a lock here so that data are properly stored at the end of the function.
|
||||
/// Lock for accessing compiler
|
||||
std::mutex local_mutex;
|
||||
std::unique_lock<std::mutex> lock(local_mutex);
|
||||
|
||||
const std::pair<Executable, llvm::ExecutionEngine *> &compileResult =
|
||||
compile(fmt::format("fn_0x%08X", block_entry.address), block_entry.address, block_entry.instructionCount);
|
||||
|
||||
if (!isAddressCommited(block_entry.address / 4))
|
||||
commitAddress(block_entry.address / 4);
|
||||
|
||||
m_executable_storage.push_back(std::unique_ptr<llvm::ExecutionEngine>(compileResult.second));
|
||||
Log() << "Associating " << (void*)(uint64_t)block_entry.address << " with ID " << m_currentId << "\n";
|
||||
FunctionCache[block_entry.address / 4] = std::make_pair(compileResult.first, m_currentId);
|
||||
m_currentId++;
|
||||
block_entry.is_compiled = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RecompilationEngine> RecompilationEngine::GetInstance() {
|
||||
std::lock_guard<std::mutex> lock(s_mutex);
|
||||
|
||||
if (s_the_instance == nullptr) {
|
||||
s_the_instance = std::shared_ptr<RecompilationEngine>(new RecompilationEngine());
|
||||
}
|
||||
|
||||
return s_the_instance;
|
||||
}
|
||||
|
||||
ppu_recompiler_llvm::CPUHybridDecoderRecompiler::CPUHybridDecoderRecompiler(PPUThread & ppu)
|
||||
: m_ppu(ppu)
|
||||
, m_interpreter(new PPUInterpreter(ppu))
|
||||
, m_decoder(m_interpreter)
|
||||
, m_recompilation_engine(RecompilationEngine::GetInstance()) {
|
||||
}
|
||||
|
||||
ppu_recompiler_llvm::CPUHybridDecoderRecompiler::~CPUHybridDecoderRecompiler() {
|
||||
}
|
||||
|
||||
u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::DecodeMemory(const u32 address) {
|
||||
ExecuteFunction(&m_ppu, 0);
|
||||
if (m_ppu.pending_exception != nullptr) {
|
||||
std::exception_ptr exn = m_ppu.pending_exception;
|
||||
m_ppu.pending_exception = nullptr;
|
||||
std::rethrow_exception(exn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteFunction(PPUThread * ppu_state, u64 context) {
|
||||
auto execution_engine = (CPUHybridDecoderRecompiler *)ppu_state->GetDecoder();
|
||||
if (ExecuteTillReturn(ppu_state, 0) == ExecutionStatus::ExecutionStatusPropagateException)
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
return ExecutionStatus::ExecutionStatusReturn;
|
||||
}
|
||||
|
||||
/// Get the branch type from a branch instruction
|
||||
static BranchType GetBranchTypeFromInstruction(u32 instruction)
|
||||
{
|
||||
u32 instructionOpcode = PPU_instr::fields::OPCD(instruction);
|
||||
u32 lk = instruction & 1;
|
||||
|
||||
if (instructionOpcode == PPU_opcodes::PPU_MainOpcodes::B ||
|
||||
instructionOpcode == PPU_opcodes::PPU_MainOpcodes::BC)
|
||||
return lk ? BranchType::FunctionCall : BranchType::LocalBranch;
|
||||
if (instructionOpcode == PPU_opcodes::PPU_MainOpcodes::G_13) {
|
||||
u32 G13Opcode = PPU_instr::fields::GD_13(instruction);
|
||||
if (G13Opcode == PPU_opcodes::G_13Opcodes::BCLR)
|
||||
return lk ? BranchType::FunctionCall : BranchType::Return;
|
||||
if (G13Opcode == PPU_opcodes::G_13Opcodes::BCCTR)
|
||||
return lk ? BranchType::FunctionCall : BranchType::LocalBranch;
|
||||
return BranchType::NonBranch;
|
||||
}
|
||||
if (instructionOpcode == PPU_opcodes::PPU_MainOpcodes::HACK && (instruction & EIF_PERFORM_BLR)) // classify HACK instruction
|
||||
return instruction & EIF_USE_BRANCH ? BranchType::FunctionCall : BranchType::Return;
|
||||
if (instructionOpcode == PPU_opcodes::PPU_MainOpcodes::HACK && (instruction & EIF_USE_BRANCH))
|
||||
return BranchType::LocalBranch;
|
||||
return BranchType::NonBranch;
|
||||
}
|
||||
|
||||
u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteTillReturn(PPUThread * ppu_state, u64 context) {
|
||||
CPUHybridDecoderRecompiler *execution_engine = (CPUHybridDecoderRecompiler *)ppu_state->GetDecoder();
|
||||
|
||||
// A block is a sequence of contiguous address.
|
||||
bool previousInstContigousAndInterp = false;
|
||||
|
||||
while (PollStatus(ppu_state) == false) {
|
||||
const Executable executable = execution_engine->m_recompilation_engine->GetCompiledExecutableIfAvailable(ppu_state->PC);
|
||||
if (executable)
|
||||
{
|
||||
auto entry = ppu_state->PC;
|
||||
u32 exit = (u32)executable(ppu_state, 0);
|
||||
if (exit == ExecutionStatus::ExecutionStatusReturn)
|
||||
{
|
||||
if (Emu.GetCPUThreadStop() == ppu_state->PC) ppu_state->fast_stop();
|
||||
return ExecutionStatus::ExecutionStatusReturn;
|
||||
}
|
||||
if (exit == ExecutionStatus::ExecutionStatusPropagateException)
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
previousInstContigousAndInterp = false;
|
||||
continue;
|
||||
}
|
||||
// if previousInstContigousAndInterp is true, ie previous step was either a compiled block or a branch inst
|
||||
// that caused a "gap" in instruction flow, we notify a new block.
|
||||
if (!previousInstContigousAndInterp)
|
||||
execution_engine->m_recompilation_engine->NotifyBlockStart(ppu_state->PC);
|
||||
u32 instruction = vm::ps3::read32(ppu_state->PC);
|
||||
u32 oldPC = ppu_state->PC;
|
||||
try
|
||||
{
|
||||
execution_engine->m_decoder.Decode(instruction);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ppu_state->pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
previousInstContigousAndInterp = (oldPC == ppu_state->PC);
|
||||
auto branch_type = ppu_state->PC != oldPC ? GetBranchTypeFromInstruction(instruction) : BranchType::NonBranch;
|
||||
ppu_state->PC += 4;
|
||||
|
||||
switch (branch_type) {
|
||||
case BranchType::Return:
|
||||
if (Emu.GetCPUThreadStop() == ppu_state->PC) ppu_state->fast_stop();
|
||||
return 0;
|
||||
case BranchType::FunctionCall: {
|
||||
u32 status = ExecuteFunction(ppu_state, 0);
|
||||
if (status == ExecutionStatus::ExecutionStatusPropagateException)
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
break;
|
||||
}
|
||||
case BranchType::LocalBranch:
|
||||
break;
|
||||
case BranchType::NonBranch:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ppu_recompiler_llvm::CPUHybridDecoderRecompiler::PollStatus(PPUThread * ppu_state) {
|
||||
try
|
||||
{
|
||||
return ppu_state->check_status();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ppu_state->pending_exception = std::current_exception();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif // LLVM_AVAILABLE
|
@ -1,946 +0,0 @@
|
||||
#ifndef PPU_LLVM_RECOMPILER_H
|
||||
#define PPU_LLVM_RECOMPILER_H
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
#define PPU_LLVM_RECOMPILER 1
|
||||
|
||||
#include <list>
|
||||
#include "Emu/Cell/PPUDecoder.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/PPUInterpreter.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/PassManager.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace ppu_recompiler_llvm {
|
||||
enum ExecutionStatus
|
||||
{
|
||||
ExecutionStatusReturn = 0, ///< Block has hit a return, caller can continue execution
|
||||
ExecutionStatusBlockEnded, ///< Block has been executed but no return was hit, at least another block must be executed before caller can continue
|
||||
ExecutionStatusPropagateException, ///< an exception was thrown
|
||||
};
|
||||
|
||||
class Compiler;
|
||||
class RecompilationEngine;
|
||||
class ExecutionEngine;
|
||||
struct PPUState;
|
||||
|
||||
enum class BranchType {
|
||||
NonBranch,
|
||||
LocalBranch,
|
||||
FunctionCall,
|
||||
Return,
|
||||
};
|
||||
|
||||
/// Pointer to an executable
|
||||
typedef u32(*Executable)(PPUThread * ppu_state, u64 context);
|
||||
|
||||
/// Parses PPU opcodes and translate them into llvm ir.
|
||||
class Compiler : protected PPUOpcodes, protected PPCDecoder {
|
||||
public:
|
||||
Compiler(llvm::LLVMContext *context, llvm::IRBuilder<> *builder, std::unordered_map<std::string, void*> &function_ptrs);
|
||||
|
||||
Compiler(const Compiler&) = delete; // Delete copy/move constructors and copy/move operators
|
||||
|
||||
virtual ~Compiler();
|
||||
|
||||
/// Create a module setting target triples and some callbacks
|
||||
static std::unique_ptr<llvm::Module> create_module(llvm::LLVMContext &llvm_context);
|
||||
|
||||
/// Create a function called name in module and populates it by translating block at start_address with instruction_count length.
|
||||
void translate_to_llvm_ir(llvm::Module *module, const std::string & name, u32 start_address, u32 instruction_count);
|
||||
|
||||
static void optimise_module(llvm::Module *module);
|
||||
|
||||
protected:
|
||||
void Decode(const u32 code) override;
|
||||
|
||||
void NULL_OP() override;
|
||||
void NOP() override;
|
||||
|
||||
void TDI(u32 to, u32 ra, s32 simm16) override;
|
||||
void TWI(u32 to, u32 ra, s32 simm16) override;
|
||||
|
||||
void MFVSCR(u32 vd) override;
|
||||
void MTVSCR(u32 vb) override;
|
||||
void VADDCUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDSBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDSHS(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDSWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUBM(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUHM(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUHS(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUWM(u32 vd, u32 va, u32 vb) override;
|
||||
void VADDUWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VAND(u32 vd, u32 va, u32 vb) override;
|
||||
void VANDC(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGSB(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGSH(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGSW(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VAVGUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VCFSX(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VCFUX(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VCMPBFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPBFP_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQFP_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUB_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUH_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPEQUW_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGEFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGEFP_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTFP_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSB(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSB_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSH(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSH_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSW(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTSW_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUB_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUH_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VCMPGTUW_(u32 vd, u32 va, u32 vb) override;
|
||||
void VCTSXS(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VCTUXS(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VEXPTEFP(u32 vd, u32 vb) override;
|
||||
void VLOGEFP(u32 vd, u32 vb) override;
|
||||
void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) override;
|
||||
void VMAXFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXSB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXSH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXSW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMAXUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMINFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINSB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINSH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINSW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMINUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMRGHB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMRGHH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMRGHW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMRGLB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMRGLH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMRGLW(u32 vd, u32 va, u32 vb) override;
|
||||
void VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VMULESB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULESH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULEUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULEUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULOSB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULOSH(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULOUB(u32 vd, u32 va, u32 vb) override;
|
||||
void VMULOUH(u32 vd, u32 va, u32 vb) override;
|
||||
void VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) override;
|
||||
void VNOR(u32 vd, u32 va, u32 vb) override;
|
||||
void VOR(u32 vd, u32 va, u32 vb) override;
|
||||
void VPERM(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VPKPX(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKSHSS(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKSHUS(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKSWSS(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKSWUS(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKUHUM(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKUHUS(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKUWUM(u32 vd, u32 va, u32 vb) override;
|
||||
void VPKUWUS(u32 vd, u32 va, u32 vb) override;
|
||||
void VREFP(u32 vd, u32 vb) override;
|
||||
void VRFIM(u32 vd, u32 vb) override;
|
||||
void VRFIN(u32 vd, u32 vb) override;
|
||||
void VRFIP(u32 vd, u32 vb) override;
|
||||
void VRFIZ(u32 vd, u32 vb) override;
|
||||
void VRLB(u32 vd, u32 va, u32 vb) override;
|
||||
void VRLH(u32 vd, u32 va, u32 vb) override;
|
||||
void VRLW(u32 vd, u32 va, u32 vb) override;
|
||||
void VRSQRTEFP(u32 vd, u32 vb) override;
|
||||
void VSEL(u32 vd, u32 va, u32 vb, u32 vc) override;
|
||||
void VSL(u32 vd, u32 va, u32 vb) override;
|
||||
void VSLB(u32 vd, u32 va, u32 vb) override;
|
||||
void VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) override;
|
||||
void VSLH(u32 vd, u32 va, u32 vb) override;
|
||||
void VSLO(u32 vd, u32 va, u32 vb) override;
|
||||
void VSLW(u32 vd, u32 va, u32 vb) override;
|
||||
void VSPLTB(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VSPLTH(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VSPLTISB(u32 vd, s32 simm5) override;
|
||||
void VSPLTISH(u32 vd, s32 simm5) override;
|
||||
void VSPLTISW(u32 vd, s32 simm5) override;
|
||||
void VSPLTW(u32 vd, u32 uimm5, u32 vb) override;
|
||||
void VSR(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRAB(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRAH(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRAW(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRB(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRH(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRO(u32 vd, u32 va, u32 vb) override;
|
||||
void VSRW(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBCUW(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBFP(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBSBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBSHS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBSWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUBM(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUHM(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUHS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUWM(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUBUWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUMSWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUM2SWS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUM4SBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUM4SHS(u32 vd, u32 va, u32 vb) override;
|
||||
void VSUM4UBS(u32 vd, u32 va, u32 vb) override;
|
||||
void VUPKHPX(u32 vd, u32 vb) override;
|
||||
void VUPKHSB(u32 vd, u32 vb) override;
|
||||
void VUPKHSH(u32 vd, u32 vb) override;
|
||||
void VUPKLPX(u32 vd, u32 vb) override;
|
||||
void VUPKLSB(u32 vd, u32 vb) override;
|
||||
void VUPKLSH(u32 vd, u32 vb) override;
|
||||
void VXOR(u32 vd, u32 va, u32 vb) override;
|
||||
void MULLI(u32 rd, u32 ra, s32 simm16) override;
|
||||
void SUBFIC(u32 rd, u32 ra, s32 simm16) override;
|
||||
void CMPLI(u32 bf, u32 l, u32 ra, u32 uimm16) override;
|
||||
void CMPI(u32 bf, u32 l, u32 ra, s32 simm16) override;
|
||||
void ADDIC(u32 rd, u32 ra, s32 simm16) override;
|
||||
void ADDIC_(u32 rd, u32 ra, s32 simm16) override;
|
||||
void ADDI(u32 rd, u32 ra, s32 simm16) override;
|
||||
void ADDIS(u32 rd, u32 ra, s32 simm16) override;
|
||||
void BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) override;
|
||||
void HACK(u32 id) override;
|
||||
void SC(u32 sc_code) override;
|
||||
void B(s32 ll, u32 aa, u32 lk) override;
|
||||
void MCRF(u32 crfd, u32 crfs) override;
|
||||
void BCLR(u32 bo, u32 bi, u32 bh, u32 lk) override;
|
||||
void CRNOR(u32 bt, u32 ba, u32 bb) override;
|
||||
void CRANDC(u32 bt, u32 ba, u32 bb) override;
|
||||
void ISYNC() override;
|
||||
void CRXOR(u32 bt, u32 ba, u32 bb) override;
|
||||
void CRNAND(u32 bt, u32 ba, u32 bb) override;
|
||||
void CRAND(u32 bt, u32 ba, u32 bb) override;
|
||||
void CREQV(u32 bt, u32 ba, u32 bb) override;
|
||||
void CRORC(u32 bt, u32 ba, u32 bb) override;
|
||||
void CROR(u32 bt, u32 ba, u32 bb) override;
|
||||
void BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) override;
|
||||
void RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, u32 rc) override;
|
||||
void RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, u32 rc) override;
|
||||
void RLWNM(u32 ra, u32 rs, u32 rb, u32 MB, u32 ME, u32 rc) override;
|
||||
void ORI(u32 rs, u32 ra, u32 uimm16) override;
|
||||
void ORIS(u32 rs, u32 ra, u32 uimm16) override;
|
||||
void XORI(u32 ra, u32 rs, u32 uimm16) override;
|
||||
void XORIS(u32 ra, u32 rs, u32 uimm16) override;
|
||||
void ANDI_(u32 ra, u32 rs, u32 uimm16) override;
|
||||
void ANDIS_(u32 ra, u32 rs, u32 uimm16) override;
|
||||
void RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) override;
|
||||
void RLDICR(u32 ra, u32 rs, u32 sh, u32 me, u32 rc) override;
|
||||
void RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) override;
|
||||
void RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) override;
|
||||
void RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, u32 is_r, u32 rc) override;
|
||||
void CMP(u32 crfd, u32 l, u32 ra, u32 rb) override;
|
||||
void TW(u32 to, u32 ra, u32 rb) override;
|
||||
void LVSL(u32 vd, u32 ra, u32 rb) override;
|
||||
void LVEBX(u32 vd, u32 ra, u32 rb) override;
|
||||
void SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void MULHDU(u32 rd, u32 ra, u32 rb, u32 rc) override;
|
||||
void ADDC(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void MULHWU(u32 rd, u32 ra, u32 rb, u32 rc) override;
|
||||
void MFOCRF(u32 a, u32 rd, u32 crm) override;
|
||||
void LWARX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LDX(u32 ra, u32 rs, u32 rb) override;
|
||||
void LWZX(u32 rd, u32 ra, u32 rb) override;
|
||||
void SLW(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void CNTLZW(u32 ra, u32 rs, u32 rc) override;
|
||||
void SLD(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void AND(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void CMPL(u32 bf, u32 l, u32 ra, u32 rb) override;
|
||||
void LVSR(u32 vd, u32 ra, u32 rb) override;
|
||||
void LVEHX(u32 vd, u32 ra, u32 rb) override;
|
||||
void SUBF(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void LDUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void DCBST(u32 ra, u32 rb) override;
|
||||
void LWZUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void CNTLZD(u32 ra, u32 rs, u32 rc) override;
|
||||
void ANDC(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void TD(u32 to, u32 ra, u32 rb) override;
|
||||
void LVEWX(u32 vd, u32 ra, u32 rb) override;
|
||||
void MULHD(u32 rd, u32 ra, u32 rb, u32 rc) override;
|
||||
void MULHW(u32 rd, u32 ra, u32 rb, u32 rc) override;
|
||||
void LDARX(u32 rd, u32 ra, u32 rb) override;
|
||||
void DCBF(u32 ra, u32 rb) override;
|
||||
void LBZX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LVX(u32 vd, u32 ra, u32 rb) override;
|
||||
void NEG(u32 rd, u32 ra, u32 oe, u32 rc) override;
|
||||
void LBZUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void NOR(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void STVEBX(u32 vs, u32 ra, u32 rb) override;
|
||||
void SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void ADDE(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void MTOCRF(u32 l, u32 crm, u32 rs) override;
|
||||
void STDX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STWCX_(u32 rs, u32 ra, u32 rb) override;
|
||||
void STWX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STVEHX(u32 vs, u32 ra, u32 rb) override;
|
||||
void STDUX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STWUX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STVEWX(u32 vs, u32 ra, u32 rb) override;
|
||||
void SUBFZE(u32 rd, u32 ra, u32 oe, u32 rc) override;
|
||||
void ADDZE(u32 rd, u32 ra, u32 oe, u32 rc) override;
|
||||
void STDCX_(u32 rs, u32 ra, u32 rb) override;
|
||||
void STBX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STVX(u32 vs, u32 ra, u32 rb) override;
|
||||
void MULLD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void SUBFME(u32 rd, u32 ra, u32 oe, u32 rc) override;
|
||||
void ADDME(u32 rd, u32 ra, u32 oe, u32 rc) override;
|
||||
void MULLW(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void DCBTST(u32 ra, u32 rb, u32 th) override;
|
||||
void STBUX(u32 rs, u32 ra, u32 rb) override;
|
||||
void ADD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void DCBT(u32 ra, u32 rb, u32 th) override;
|
||||
void LHZX(u32 rd, u32 ra, u32 rb) override;
|
||||
void EQV(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void ECIWX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LHZUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void XOR(u32 rs, u32 ra, u32 rb, u32 rc) override;
|
||||
void MFSPR(u32 rd, u32 spr) override;
|
||||
void LWAX(u32 rd, u32 ra, u32 rb) override;
|
||||
void DST(u32 ra, u32 rb, u32 strm, u32 t) override;
|
||||
void LHAX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LVXL(u32 vd, u32 ra, u32 rb) override;
|
||||
void MFTB(u32 rd, u32 spr) override;
|
||||
void LWAUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void DSTST(u32 ra, u32 rb, u32 strm, u32 t) override;
|
||||
void LHAUX(u32 rd, u32 ra, u32 rb) override;
|
||||
void STHX(u32 rs, u32 ra, u32 rb) override;
|
||||
void ORC(u32 rs, u32 ra, u32 rb, u32 rc) override;
|
||||
void ECOWX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STHUX(u32 rs, u32 ra, u32 rb) override;
|
||||
void OR(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void MTSPR(u32 spr, u32 rs) override;
|
||||
void DCBI(u32 ra, u32 rb) override;
|
||||
void NAND(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void STVXL(u32 vs, u32 ra, u32 rb) override;
|
||||
void DIVD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void DIVW(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) override;
|
||||
void LVLX(u32 vd, u32 ra, u32 rb) override;
|
||||
void LDBRX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LSWX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LWBRX(u32 rd, u32 ra, u32 rb) override;
|
||||
void LFSX(u32 frd, u32 ra, u32 rb) override;
|
||||
void SRW(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void SRD(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void LVRX(u32 vd, u32 ra, u32 rb) override;
|
||||
void LSWI(u32 rd, u32 ra, u32 nb) override;
|
||||
void LFSUX(u32 frd, u32 ra, u32 rb) override;
|
||||
void SYNC(u32 l) override;
|
||||
void LFDX(u32 frd, u32 ra, u32 rb) override;
|
||||
void LFDUX(u32 frd, u32 ra, u32 rb) override;
|
||||
void STVLX(u32 vs, u32 ra, u32 rb) override;
|
||||
void STDBRX(u32 rd, u32 ra, u32 rb) override;
|
||||
void STSWX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STWBRX(u32 rs, u32 ra, u32 rb) override;
|
||||
void STFSX(u32 frs, u32 ra, u32 rb) override;
|
||||
void STVRX(u32 vs, u32 ra, u32 rb) override;
|
||||
void STFSUX(u32 frs, u32 ra, u32 rb) override;
|
||||
void STSWI(u32 rd, u32 ra, u32 nb) override;
|
||||
void STFDX(u32 frs, u32 ra, u32 rb) override;
|
||||
void STFDUX(u32 frs, u32 ra, u32 rb) override;
|
||||
void LVLXL(u32 vd, u32 ra, u32 rb) override;
|
||||
void LHBRX(u32 rd, u32 ra, u32 rb) override;
|
||||
void SRAW(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void SRAD(u32 ra, u32 rs, u32 rb, u32 rc) override;
|
||||
void LVRXL(u32 vd, u32 ra, u32 rb) override;
|
||||
void DSS(u32 strm, u32 a) override;
|
||||
void SRAWI(u32 ra, u32 rs, u32 sh, u32 rc) override;
|
||||
void SRADI1(u32 ra, u32 rs, u32 sh, u32 rc) override;
|
||||
void SRADI2(u32 ra, u32 rs, u32 sh, u32 rc) override;
|
||||
void EIEIO() override;
|
||||
void STVLXL(u32 vs, u32 ra, u32 rb) override;
|
||||
void STHBRX(u32 rs, u32 ra, u32 rb) override;
|
||||
void EXTSH(u32 ra, u32 rs, u32 rc) override;
|
||||
void STVRXL(u32 sd, u32 ra, u32 rb) override;
|
||||
void EXTSB(u32 ra, u32 rs, u32 rc) override;
|
||||
void STFIWX(u32 frs, u32 ra, u32 rb) override;
|
||||
void EXTSW(u32 ra, u32 rs, u32 rc) override;
|
||||
void ICBI(u32 ra, u32 rb) override;
|
||||
void DCBZ(u32 ra, u32 rb) override;
|
||||
void LWZ(u32 rd, u32 ra, s32 d) override;
|
||||
void LWZU(u32 rd, u32 ra, s32 d) override;
|
||||
void LBZ(u32 rd, u32 ra, s32 d) override;
|
||||
void LBZU(u32 rd, u32 ra, s32 d) override;
|
||||
void STW(u32 rs, u32 ra, s32 d) override;
|
||||
void STWU(u32 rs, u32 ra, s32 d) override;
|
||||
void STB(u32 rs, u32 ra, s32 d) override;
|
||||
void STBU(u32 rs, u32 ra, s32 d) override;
|
||||
void LHZ(u32 rd, u32 ra, s32 d) override;
|
||||
void LHZU(u32 rd, u32 ra, s32 d) override;
|
||||
void LHA(u32 rs, u32 ra, s32 d) override;
|
||||
void LHAU(u32 rs, u32 ra, s32 d) override;
|
||||
void STH(u32 rs, u32 ra, s32 d) override;
|
||||
void STHU(u32 rs, u32 ra, s32 d) override;
|
||||
void LMW(u32 rd, u32 ra, s32 d) override;
|
||||
void STMW(u32 rs, u32 ra, s32 d) override;
|
||||
void LFS(u32 frd, u32 ra, s32 d) override;
|
||||
void LFSU(u32 frd, u32 ra, s32 d) override;
|
||||
void LFD(u32 frd, u32 ra, s32 d) override;
|
||||
void LFDU(u32 frd, u32 ra, s32 d) override;
|
||||
void STFS(u32 frs, u32 ra, s32 d) override;
|
||||
void STFSU(u32 frs, u32 ra, s32 d) override;
|
||||
void STFD(u32 frs, u32 ra, s32 d) override;
|
||||
void STFDU(u32 frs, u32 ra, s32 d) override;
|
||||
void LD(u32 rd, u32 ra, s32 ds) override;
|
||||
void LDU(u32 rd, u32 ra, s32 ds) override;
|
||||
void LWA(u32 rd, u32 ra, s32 ds) override;
|
||||
void FDIVS(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FSUBS(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FADDS(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FSQRTS(u32 frd, u32 frb, u32 rc) override;
|
||||
void FRES(u32 frd, u32 frb, u32 rc) override;
|
||||
void FMULS(u32 frd, u32 fra, u32 frc, u32 rc) override;
|
||||
void FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void STD(u32 rs, u32 ra, s32 ds) override;
|
||||
void STDU(u32 rs, u32 ra, s32 ds) override;
|
||||
void MTFSB1(u32 bt, u32 rc) override;
|
||||
void MCRFS(u32 bf, u32 bfa) override;
|
||||
void MTFSB0(u32 bt, u32 rc) override;
|
||||
void MTFSFI(u32 crfd, u32 i, u32 rc) override;
|
||||
void MFFS(u32 frd, u32 rc) override;
|
||||
void MTFSF(u32 flm, u32 frb, u32 rc) override;
|
||||
|
||||
void FCMPU(u32 bf, u32 fra, u32 frb) override;
|
||||
void FRSP(u32 frd, u32 frb, u32 rc) override;
|
||||
void FCTIW(u32 frd, u32 frb, u32 rc) override;
|
||||
void FCTIWZ(u32 frd, u32 frb, u32 rc) override;
|
||||
void FDIV(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FSUB(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FADD(u32 frd, u32 fra, u32 frb, u32 rc) override;
|
||||
void FSQRT(u32 frd, u32 frb, u32 rc) override;
|
||||
void FSEL(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FMUL(u32 frd, u32 fra, u32 frc, u32 rc) override;
|
||||
void FRSQRTE(u32 frd, u32 frb, u32 rc) override;
|
||||
void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) override;
|
||||
void FCMPO(u32 crfd, u32 fra, u32 frb) override;
|
||||
void FNEG(u32 frd, u32 frb, u32 rc) override;
|
||||
void FMR(u32 frd, u32 frb, u32 rc) override;
|
||||
void FNABS(u32 frd, u32 frb, u32 rc) override;
|
||||
void FABS(u32 frd, u32 frb, u32 rc) override;
|
||||
void FCTID(u32 frd, u32 frb, u32 rc) override;
|
||||
void FCTIDZ(u32 frd, u32 frb, u32 rc) override;
|
||||
void FCFID(u32 frd, u32 frb, u32 rc) override;
|
||||
|
||||
void UNK(const u32 code, const u32 opcode, const u32 gcode) override;
|
||||
|
||||
/// Utility function creating a function called name with Executable signature
|
||||
void initiate_function(const std::string &name);
|
||||
|
||||
protected:
|
||||
/// State of a compilation task
|
||||
struct CompileTaskState {
|
||||
enum Args {
|
||||
State,
|
||||
Context,
|
||||
MaxArgs,
|
||||
};
|
||||
|
||||
/// The LLVM function for the compilation task
|
||||
llvm::Function * function;
|
||||
|
||||
/// Args of the LLVM function
|
||||
llvm::Value * args[MaxArgs];
|
||||
|
||||
/// Address of the current instruction being compiled
|
||||
u32 current_instruction_address;
|
||||
|
||||
/// A flag used to detect branch instructions.
|
||||
/// This is set to false at the start of compilation of an instruction.
|
||||
/// If a branch instruction is encountered, this is set to true by the decode function.
|
||||
bool hit_branch_instruction;
|
||||
};
|
||||
|
||||
/// The function that will be called to execute unknown functions
|
||||
llvm::Function * m_execute_unknown_function;
|
||||
|
||||
/// The executable that will be called to execute unknown blocks
|
||||
llvm::Function * m_execute_unknown_block;
|
||||
|
||||
/// Maps function name to executable memory pointer
|
||||
std::unordered_map<std::string, void*> &m_executable_map;
|
||||
|
||||
/// LLVM context
|
||||
llvm::LLVMContext * m_llvm_context;
|
||||
|
||||
/// LLVM IR builder
|
||||
llvm::IRBuilder<> * m_ir_builder;
|
||||
|
||||
/// Module to which all generated code is output to
|
||||
llvm::Module * m_module;
|
||||
|
||||
/// LLVM type of the functions genreated by the compiler
|
||||
llvm::FunctionType * m_compiled_function_type;
|
||||
|
||||
/// State of the current compilation task
|
||||
CompileTaskState m_state;
|
||||
|
||||
/// Get the name of the basic block for the specified address
|
||||
std::string GetBasicBlockNameFromAddress(u32 address, const std::string & suffix = "") const;
|
||||
|
||||
/// Get the address of a basic block from its name
|
||||
u32 GetAddressFromBasicBlockName(const std::string & name) const;
|
||||
|
||||
/// Get the basic block in for the specified address.
|
||||
llvm::BasicBlock * GetBasicBlockFromAddress(u32 address, const std::string & suffix = "", bool create_if_not_exist = true);
|
||||
|
||||
/// Get a bit
|
||||
llvm::Value * GetBit(llvm::Value * val, u32 n);
|
||||
|
||||
/// Clear a bit
|
||||
llvm::Value * ClrBit(llvm::Value * val, u32 n);
|
||||
|
||||
/// Set a bit
|
||||
llvm::Value * SetBit(llvm::Value * val, u32 n, llvm::Value * bit, bool doClear = true);
|
||||
|
||||
/// Get a nibble
|
||||
llvm::Value * GetNibble(llvm::Value * val, u32 n);
|
||||
|
||||
/// Clear a nibble
|
||||
llvm::Value * ClrNibble(llvm::Value * val, u32 n);
|
||||
|
||||
/// Set a nibble
|
||||
llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * nibble, bool doClear = true);
|
||||
|
||||
/// Set a nibble
|
||||
llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3, bool doClear = true);
|
||||
|
||||
/// Load PC
|
||||
llvm::Value * GetPc();
|
||||
|
||||
/// Set PC
|
||||
void SetPc(llvm::Value * val_ix);
|
||||
|
||||
/// Load GPR
|
||||
llvm::Value * GetGpr(u32 r, u32 num_bits = 64);
|
||||
|
||||
/// Set GPR
|
||||
void SetGpr(u32 r, llvm::Value * val_x64);
|
||||
|
||||
/// Load CR
|
||||
llvm::Value * GetCr();
|
||||
|
||||
/// Load CR and get field CRn
|
||||
llvm::Value * GetCrField(u32 n);
|
||||
|
||||
/// Set CR
|
||||
void SetCr(llvm::Value * val_x32);
|
||||
|
||||
/// Set CR field
|
||||
void SetCrField(u32 n, llvm::Value * field);
|
||||
|
||||
/// Set CR field
|
||||
void SetCrField(u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3);
|
||||
|
||||
/// Set CR field based on signed comparison
|
||||
void SetCrFieldSignedCmp(u32 n, llvm::Value * a, llvm::Value * b);
|
||||
|
||||
/// Set CR field based on unsigned comparison
|
||||
void SetCrFieldUnsignedCmp(u32 n, llvm::Value * a, llvm::Value * b);
|
||||
|
||||
/// Set CR6 based on the result of the vector compare instruction
|
||||
void SetCr6AfterVectorCompare(u32 vr);
|
||||
|
||||
/// Get LR
|
||||
llvm::Value * GetLr();
|
||||
|
||||
/// Set LR
|
||||
void SetLr(llvm::Value * val_x64);
|
||||
|
||||
/// Get CTR
|
||||
llvm::Value * GetCtr();
|
||||
|
||||
/// Set CTR
|
||||
void SetCtr(llvm::Value * val_x64);
|
||||
|
||||
/// Load XER and convert it to an i64
|
||||
llvm::Value * GetXer();
|
||||
|
||||
/// Load XER and return the CA bit
|
||||
llvm::Value * GetXerCa();
|
||||
|
||||
/// Load XER and return the SO bit
|
||||
llvm::Value * GetXerSo();
|
||||
|
||||
/// Set XER
|
||||
void SetXer(llvm::Value * val_x64);
|
||||
|
||||
/// Set the CA bit of XER
|
||||
void SetXerCa(llvm::Value * ca);
|
||||
|
||||
/// Set the SO bit of XER
|
||||
void SetXerSo(llvm::Value * so);
|
||||
|
||||
/// Get VRSAVE
|
||||
llvm::Value * GetVrsave();
|
||||
|
||||
/// Set VRSAVE
|
||||
void SetVrsave(llvm::Value * val_x64);
|
||||
|
||||
/// Load FPSCR
|
||||
llvm::Value * GetFpscr();
|
||||
|
||||
/// Set FPSCR
|
||||
void SetFpscr(llvm::Value * val_x32);
|
||||
|
||||
/// Get FPR
|
||||
llvm::Value * GetFpr(u32 r, u32 bits = 64, bool as_int = false);
|
||||
|
||||
/// Set FPR
|
||||
void SetFpr(u32 r, llvm::Value * val);
|
||||
|
||||
/// Load VSCR
|
||||
llvm::Value * GetVscr();
|
||||
|
||||
/// Set VSCR
|
||||
void SetVscr(llvm::Value * val_x32);
|
||||
|
||||
/// Load VR
|
||||
llvm::Value * GetVr(u32 vr);
|
||||
|
||||
/// Load VR and convert it to an integer vector
|
||||
llvm::Value * GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits);
|
||||
|
||||
/// Load VR and convert it to a float vector with 4 elements
|
||||
llvm::Value * GetVrAsFloatVec(u32 vr);
|
||||
|
||||
/// Load VR and convert it to a double vector with 2 elements
|
||||
llvm::Value * GetVrAsDoubleVec(u32 vr);
|
||||
|
||||
/// Set VR to the specified value
|
||||
void SetVr(u32 vr, llvm::Value * val_x128);
|
||||
|
||||
/// Check condition for branch instructions
|
||||
llvm::Value * CheckBranchCondition(u32 bo, u32 bi);
|
||||
|
||||
/// Create IR for a branch instruction
|
||||
void CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool lk, bool target_is_lr = false);
|
||||
|
||||
/// Read from memory
|
||||
llvm::Value * ReadMemory(llvm::Value * addr_i64, u32 bits, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true);
|
||||
|
||||
/// Write to memory
|
||||
void WriteMemory(llvm::Value * addr_i64, llvm::Value * val_ix, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true);
|
||||
|
||||
/// Convert a C++ type to an LLVM type
|
||||
template<class T>
|
||||
llvm::Type * CppToLlvmType() {
|
||||
if (std::is_void<T>::value) {
|
||||
return m_ir_builder->getVoidTy();
|
||||
}
|
||||
else if (std::is_same<T, long long>::value || std::is_same<T, unsigned long long>::value) {
|
||||
return m_ir_builder->getInt64Ty();
|
||||
}
|
||||
else if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value) {
|
||||
return m_ir_builder->getInt32Ty();
|
||||
}
|
||||
else if (std::is_same<T, short>::value || std::is_same<T, unsigned short>::value) {
|
||||
return m_ir_builder->getInt16Ty();
|
||||
}
|
||||
else if (std::is_same<T, char>::value || std::is_same<T, unsigned char>::value) {
|
||||
return m_ir_builder->getInt8Ty();
|
||||
}
|
||||
else if (std::is_same<T, float>::value) {
|
||||
return m_ir_builder->getFloatTy();
|
||||
}
|
||||
else if (std::is_same<T, double>::value) {
|
||||
return m_ir_builder->getDoubleTy();
|
||||
}
|
||||
else if (std::is_same<T, bool>::value) {
|
||||
return m_ir_builder->getInt1Ty();
|
||||
}
|
||||
else if (std::is_pointer<T>::value) {
|
||||
return m_ir_builder->getInt8PtrTy();
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Call a function
|
||||
template<class ReturnType, class... Args>
|
||||
llvm::Value * Call(const char * name, Args... args) {
|
||||
auto fn = m_module->getFunction(name);
|
||||
if (!fn) {
|
||||
std::vector<llvm::Type *> fn_args_type = { args->getType()... };
|
||||
auto fn_type = llvm::FunctionType::get(CppToLlvmType<ReturnType>(), fn_args_type, false);
|
||||
fn = llvm::cast<llvm::Function>(m_module->getOrInsertFunction(name, fn_type));
|
||||
fn->setCallingConv(llvm::CallingConv::X86_64_Win64);
|
||||
// Create an entry in m_executable_map that will be populated outside of compiler
|
||||
(void)m_executable_map[name];
|
||||
}
|
||||
|
||||
std::vector<llvm::Value *> fn_args = { args... };
|
||||
return m_ir_builder->CreateCall(fn, fn_args);
|
||||
}
|
||||
|
||||
/// Handle compilation errors
|
||||
void CompilationError(const std::string & error);
|
||||
|
||||
/// A mask used in rotate instructions
|
||||
static u64 s_rotate_mask[64][64];
|
||||
|
||||
/// A flag indicating whether s_rotate_mask has been initialised or not
|
||||
static bool s_rotate_mask_inited;
|
||||
|
||||
/// Initialse s_rotate_mask
|
||||
static void InitRotateMask();
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages block compilation.
|
||||
* PPUInterpreter1 execution is traced (using Tracer class)
|
||||
* Periodically RecompilationEngine process traces result to find blocks
|
||||
* whose compilation can improve performances.
|
||||
* It then builds them asynchroneously and update the executable mapping
|
||||
* using atomic based locks to avoid undefined behavior.
|
||||
**/
|
||||
class RecompilationEngine final : public named_thread_t {
|
||||
friend class CPUHybridDecoderRecompiler;
|
||||
public:
|
||||
virtual ~RecompilationEngine() override;
|
||||
|
||||
/**
|
||||
* Get the executable for the specified address if a compiled version is
|
||||
* available, otherwise returns nullptr.
|
||||
**/
|
||||
const Executable GetCompiledExecutableIfAvailable(u32 address) const;
|
||||
|
||||
/// Notify the recompilation engine about a newly detected block start.
|
||||
void NotifyBlockStart(u32 address);
|
||||
|
||||
/// Log
|
||||
llvm::raw_fd_ostream & Log();
|
||||
|
||||
std::string get_name() const override { return "PPU Recompilation Engine"; }
|
||||
|
||||
void on_task() override;
|
||||
|
||||
/// Get a pointer to the instance of this class
|
||||
static std::shared_ptr<RecompilationEngine> GetInstance();
|
||||
|
||||
private:
|
||||
/// An entry in the block table
|
||||
struct BlockEntry {
|
||||
/// Start address
|
||||
u32 address;
|
||||
|
||||
/// Number of times this block was hit
|
||||
u32 num_hits;
|
||||
|
||||
/// Indicates whether this function has been analysed or not
|
||||
bool is_analysed;
|
||||
|
||||
/// Indicates whether the block has been compiled or not
|
||||
bool is_compiled;
|
||||
|
||||
/// Indicate wheter the block is a function that can be completly compiled
|
||||
/// that is, that has a clear "return" semantic and no indirect branch
|
||||
bool is_compilable_function;
|
||||
|
||||
/// If the analysis was successfull, how long the block is.
|
||||
u32 instructionCount;
|
||||
|
||||
/// If the analysis was successfull, which function does it call.
|
||||
std::set<u32> calledFunctions;
|
||||
|
||||
BlockEntry(u32 start_address)
|
||||
: num_hits(0)
|
||||
, address(start_address)
|
||||
, is_compiled(false)
|
||||
, is_analysed(false)
|
||||
, is_compilable_function(false)
|
||||
, instructionCount(0) {
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
return fmt::format("0x%08X: NumHits=%u, IsCompiled=%c",
|
||||
address, num_hits, is_compiled ? 'Y' : 'N');
|
||||
}
|
||||
|
||||
bool operator == (const BlockEntry & other) const {
|
||||
return address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
/// Log
|
||||
llvm::raw_fd_ostream * m_log;
|
||||
|
||||
/// Lock for accessing m_pending_address_start. TODO: Eliminate this and use a lock-free queue.
|
||||
std::mutex m_pending_address_start_lock;
|
||||
|
||||
/// Queue of block start address to process
|
||||
std::list<u32> m_pending_address_start;
|
||||
|
||||
/// Block table
|
||||
std::unordered_map<u32, BlockEntry> m_block_table;
|
||||
|
||||
int m_currentId;
|
||||
|
||||
/// (function, id).
|
||||
typedef std::pair<Executable, u32> ExecutableStorageType;
|
||||
|
||||
/// Virtual memory allocated array.
|
||||
/// Store pointer to every compiled function/block and a unique Id.
|
||||
/// We need to map every instruction in PS3 Ram so it's a big table
|
||||
/// But a lot of it won't be accessed. Fortunatly virtual memory help here...
|
||||
ExecutableStorageType* FunctionCache;
|
||||
|
||||
// Bitfield recording page status in FunctionCache reserved memory.
|
||||
char *FunctionCachePagesCommited;
|
||||
|
||||
bool isAddressCommited(u32) const;
|
||||
void commitAddress(u32);
|
||||
|
||||
/// vector storing all exec engine
|
||||
std::vector<std::unique_ptr<llvm::ExecutionEngine> > m_executable_storage;
|
||||
|
||||
|
||||
/// LLVM context
|
||||
llvm::LLVMContext &m_llvm_context;
|
||||
|
||||
/// LLVM IR builder
|
||||
llvm::IRBuilder<> m_ir_builder;
|
||||
|
||||
/**
|
||||
* Compile a code fragment described by a cfg and return an executable and the ExecutionEngine storing it
|
||||
* Pointer to function can be retrieved with getPointerToFunction
|
||||
*/
|
||||
std::pair<Executable, llvm::ExecutionEngine *> compile(const std::string & name, u32 start_address, u32 instruction_count);
|
||||
|
||||
/// The time at which the m_address_to_ordinal cache was last cleared
|
||||
std::chrono::high_resolution_clock::time_point m_last_cache_clear_time;
|
||||
|
||||
RecompilationEngine();
|
||||
|
||||
RecompilationEngine(const RecompilationEngine&) = delete; // Delete copy/move constructors and copy/move operators
|
||||
|
||||
/// Increase usage counter for block starting at addr and compile it if threshold was reached.
|
||||
/// Returns true if block was compiled
|
||||
bool IncreaseHitCounterAndBuild(u32 addr);
|
||||
|
||||
/**
|
||||
* Analyse block to get useful info (function called, has indirect branch...)
|
||||
* This code is inspired from Dolphin PPC Analyst
|
||||
* Return true if analysis is successful.
|
||||
*/
|
||||
bool AnalyseBlock(BlockEntry &functionData, size_t maxSize = 10000);
|
||||
|
||||
/// Compile a block
|
||||
void CompileBlock(BlockEntry & block_entry);
|
||||
|
||||
/// Mutex used to prevent multiple creation
|
||||
static std::mutex s_mutex;
|
||||
|
||||
/// The instance
|
||||
static std::shared_ptr<RecompilationEngine> s_the_instance;
|
||||
};
|
||||
|
||||
/**
|
||||
* PPU execution engine
|
||||
* Relies on PPUInterpreter1 to execute uncompiled code.
|
||||
* Traces execution to determine which block to compile.
|
||||
* Use LLVM to compile block into native code.
|
||||
*/
|
||||
class CPUHybridDecoderRecompiler : public CPUDecoder {
|
||||
friend class RecompilationEngine;
|
||||
friend class Compiler;
|
||||
public:
|
||||
CPUHybridDecoderRecompiler(PPUThread & ppu);
|
||||
|
||||
CPUHybridDecoderRecompiler(const CPUHybridDecoderRecompiler&) = delete; // Delete copy/move constructors and copy/move operators
|
||||
|
||||
virtual ~CPUHybridDecoderRecompiler();
|
||||
|
||||
u32 DecodeMemory(const u32 address) override;
|
||||
|
||||
private:
|
||||
/// PPU processor context
|
||||
PPUThread & m_ppu;
|
||||
|
||||
/// PPU Interpreter
|
||||
PPUInterpreter * m_interpreter;
|
||||
|
||||
/// PPU instruction Decoder
|
||||
PPUDecoder m_decoder;
|
||||
|
||||
/// Recompilation engine
|
||||
std::shared_ptr<RecompilationEngine> m_recompilation_engine;
|
||||
|
||||
/// Execute a function
|
||||
static u32 ExecuteFunction(PPUThread * ppu_state, u64 context);
|
||||
|
||||
/// Execute till the current function returns
|
||||
static u32 ExecuteTillReturn(PPUThread * ppu_state, u64 context);
|
||||
|
||||
/// Check thread status. Returns true if the thread must exit.
|
||||
static bool PollStatus(PPUThread * ppu_state);
|
||||
};
|
||||
|
||||
class CustomSectionMemoryManager : public llvm::SectionMemoryManager {
|
||||
private:
|
||||
std::unordered_map<std::string, void*> &executableMap;
|
||||
public:
|
||||
CustomSectionMemoryManager(std::unordered_map<std::string, void*> &map) :
|
||||
executableMap(map)
|
||||
{}
|
||||
~CustomSectionMemoryManager() override {}
|
||||
|
||||
virtual uint64_t getSymbolAddress(const std::string &Name) override
|
||||
{
|
||||
std::unordered_map<std::string, void*>::const_iterator It = executableMap.find(Name);
|
||||
if (It != executableMap.end())
|
||||
return (uint64_t)It->second;
|
||||
return getSymbolAddressInProcess(Name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LLVM_AVAILABLE
|
||||
#endif // PPU_LLVM_RECOMPILER_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,192 +0,0 @@
|
||||
#pragma once
|
||||
/*#include "PPUInstrTable.h"
|
||||
#include "Loader/ELF64.h"
|
||||
|
||||
enum ArgType
|
||||
{
|
||||
ARG_ERR = 0,
|
||||
ARG_NUM = 1 << 0,
|
||||
ARG_NUM16 = 1 << 1,
|
||||
ARG_TXT = 1 << 2,
|
||||
ARG_REG_R = 1 << 3,
|
||||
ARG_REG_F = 1 << 4,
|
||||
ARG_REG_V = 1 << 5,
|
||||
ARG_REG_CR = 1 << 6,
|
||||
ARG_BRANCH = 1 << 7,
|
||||
ARG_INSTR = 1 << 8,
|
||||
ARG_IMM = ARG_NUM | ARG_NUM16 | ARG_BRANCH,
|
||||
};
|
||||
|
||||
struct Arg
|
||||
{
|
||||
std::string string;
|
||||
u32 value;
|
||||
ArgType type;
|
||||
|
||||
Arg(const std::string& _string, const u32 _value = 0, const ArgType _type = ARG_ERR)
|
||||
: string(_string)
|
||||
, value(_value)
|
||||
, type(_type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SectionInfo
|
||||
{
|
||||
Elf64_Shdr shdr;
|
||||
std::string name;
|
||||
std::vector<u8> code;
|
||||
u32 section_num;
|
||||
|
||||
SectionInfo(const std::string& name);
|
||||
~SectionInfo();
|
||||
|
||||
void SetDataSize(u32 size, u32 align = 0);
|
||||
};
|
||||
|
||||
struct ProgramInfo
|
||||
{
|
||||
std::vector<u8> code;
|
||||
Elf64_Phdr phdr;
|
||||
bool is_preload;
|
||||
|
||||
ProgramInfo()
|
||||
{
|
||||
is_preload = false;
|
||||
memset(&phdr, 0, sizeof(Elf64_Phdr));
|
||||
}
|
||||
};
|
||||
|
||||
class CompilePPUProgram
|
||||
{
|
||||
struct Branch
|
||||
{
|
||||
std::string m_name;
|
||||
s32 m_pos;
|
||||
s32 m_id;
|
||||
s32 m_addr;
|
||||
|
||||
Branch(const std::string& name, s32 pos)
|
||||
: m_name(name)
|
||||
, m_pos(pos)
|
||||
, m_id(-1)
|
||||
, m_addr(-1)
|
||||
{
|
||||
}
|
||||
|
||||
Branch(const std::string& name, u32 id, u32 addr)
|
||||
: m_name(name)
|
||||
, m_pos(-1)
|
||||
, m_id(id)
|
||||
, m_addr(addr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bool m_analyze;
|
||||
s64 p;
|
||||
u64 m_line;
|
||||
const std::string& m_asm;
|
||||
wxTextCtrl* m_asm_list;
|
||||
wxTextCtrl* m_hex_list;
|
||||
wxTextCtrl* m_err_list;
|
||||
bool m_error;
|
||||
std::vector<u32> m_code;
|
||||
bool m_end_args;
|
||||
std::vector<Branch> m_branches;
|
||||
s32 m_branch_pos;
|
||||
u32 m_text_addr;
|
||||
std::string m_file_path;
|
||||
|
||||
struct SpData
|
||||
{
|
||||
std::string m_data;
|
||||
u32 m_addr;
|
||||
|
||||
SpData(const std::string& data, u32 addr)
|
||||
: m_data(data)
|
||||
, m_addr(addr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<SpData> m_sp_string;
|
||||
std::vector<Arg> m_args;
|
||||
u32 m_cur_arg;
|
||||
|
||||
public:
|
||||
CompilePPUProgram(
|
||||
const std::string& asm_,
|
||||
const std::string& file_path = "",
|
||||
wxTextCtrl* asm_list = nullptr,
|
||||
wxTextCtrl* hex_list = nullptr,
|
||||
wxTextCtrl* err_list = nullptr,
|
||||
bool analyze = false);
|
||||
|
||||
static bool IsSkip(const char c);
|
||||
static bool IsCommit(const char c);
|
||||
|
||||
protected:
|
||||
bool IsEnd() const;
|
||||
bool IsEndLn(const char c) const;
|
||||
|
||||
void WriteHex(const std::string& text);
|
||||
void WriteError(const std::string& error);
|
||||
|
||||
char NextChar();
|
||||
void NextLn();
|
||||
void EndLn();
|
||||
|
||||
void FirstChar();
|
||||
void PrevArg();
|
||||
|
||||
bool GetOp(std::string& result);
|
||||
int GetArg(std::string& result, bool func = false);
|
||||
|
||||
bool CheckEnd(bool show_err = true);
|
||||
|
||||
void DetectArgInfo(Arg& arg);
|
||||
void LoadArgs();
|
||||
u32 GetBranchValue(const std::string& branch) const;
|
||||
|
||||
bool SetNextArgType(u32 types, bool show_err = true);
|
||||
bool SetNextArgBranch(u8 aa, bool show_err = true);
|
||||
|
||||
public:
|
||||
static bool IsBranchOp(const std::string& op);
|
||||
static bool IsFuncOp(const std::string& op);
|
||||
|
||||
enum SP_TYPE
|
||||
{
|
||||
SP_ERR,
|
||||
SP_INT,
|
||||
SP_STRING,
|
||||
SP_STRLEN,
|
||||
SP_BUF,
|
||||
SP_SRL,
|
||||
SP_SRR,
|
||||
SP_MUL,
|
||||
SP_DIV,
|
||||
SP_ADD,
|
||||
SP_SUB,
|
||||
SP_AND,
|
||||
SP_OR,
|
||||
SP_XOR,
|
||||
SP_NOT,
|
||||
SP_NOR,
|
||||
};
|
||||
|
||||
static SP_TYPE GetSpType(const std::string& op);
|
||||
static std::string GetSpStyle(const SP_TYPE sp);
|
||||
|
||||
static bool IsSpOp(const std::string& op);
|
||||
|
||||
protected:
|
||||
Branch& GetBranch(const std::string& name);
|
||||
void SetSp(const std::string& name, u32 addr, bool create);
|
||||
void LoadSp(const std::string& op, Elf64_Shdr& s_opd);
|
||||
|
||||
public:
|
||||
void Compile();
|
||||
};
|
||||
*/
|
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class PPUThread;
|
||||
|
||||
class CallbackManager
|
||||
{
|
||||
using check_cb_t = std::function<s32(PPUThread&)>;
|
||||
using async_cb_t = std::function<void(PPUThread&)>;
|
||||
|
||||
std::mutex m_mutex;
|
||||
|
||||
std::queue<check_cb_t> m_check_cb;
|
||||
std::queue<async_cb_t> m_async_cb;
|
||||
|
||||
std::shared_ptr<PPUThread> m_cb_thread;
|
||||
|
||||
public:
|
||||
// Register checked callback
|
||||
void Register(check_cb_t func);
|
||||
|
||||
// Register async callback, called in callback thread
|
||||
void Async(async_cb_t func);
|
||||
|
||||
// Get one registered callback
|
||||
check_cb_t Check();
|
||||
|
||||
void Init();
|
||||
|
||||
void Clear();
|
||||
};
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user