rpcsx/rpcs3/Emu/Cell/PPULLVMRecompiler.h
Vincent Lejeune ecb3d93918 PPU/LLVM: Remove lock in GetCompiledExecutableIfAvailable
This should increase performance a lot in some case.
2015-09-15 19:21:40 +02:00

980 lines
37 KiB
C++

#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);
/// PPU compiler that uses LLVM for code generation and optimization
class Compiler : protected PPUOpcodes, protected PPCDecoder {
public:
struct Stats {
/// Time spent building the LLVM IR
std::chrono::nanoseconds ir_build_time;
/// Time spent optimizing
std::chrono::nanoseconds optimization_time;
/// Time spent translating LLVM IR to machine code
std::chrono::nanoseconds translation_time;
/// Total time
std::chrono::nanoseconds total_time;
};
Compiler(RecompilationEngine & recompilation_engine, const Executable execute_unknown_function,
const Executable execute_unknown_block, bool(*poll_status_function)(PPUThread * ppu_state));
Compiler(const Compiler & other) = delete;
Compiler(Compiler && other) = delete;
virtual ~Compiler();
Compiler & operator = (const Compiler & other) = delete;
Compiler & operator = (Compiler && other) = delete;
/**
* 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);
/// Retrieve compiler stats
Stats GetStats();
/// Execute all tests
void RunAllTests();
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;
private:
/// 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;
};
/// Recompilation engine
RecompilationEngine & m_recompilation_engine;
/// The function that should be called to check the status of the thread
bool(*m_poll_status_function)(PPUThread * ppu_state);
/// 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, Executable> m_executableMap;
/// 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;
/// Compiler stats
Stats m_stats;
/// 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 Func, class... Args>
llvm::Value * Call(const char * name, Func function, 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);
// Note: not threadsafe
m_executableMap[name] = (Executable)(void *&)function;
}
std::vector<llvm::Value *> fn_args = { args... };
return m_ir_builder->CreateCall(fn, fn_args);
}
/// Test an instruction against the interpreter
template <class... Args>
void VerifyInstructionAgainstInterpreter(const char * name, void (Compiler::*recomp_fn)(Args...), void (PPUInterpreter::*interp_fn)(Args...), PPUState & input_state, Args... args);
/// Excute a test
void RunTest(const char * name, std::function<void()> test_case, std::function<void()> input, std::function<bool(std::string & msg)> check_result);
/// 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 : protected 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();
void Task();
/// 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;
/// 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;
/// PPU Compiler
Compiler m_compiler;
RecompilationEngine();
RecompilationEngine(const RecompilationEngine & other) = delete;
RecompilationEngine(RecompilationEngine && other) = delete;
RecompilationEngine & operator = (const RecompilationEngine & other) = delete;
RecompilationEngine & operator = (RecompilationEngine && other) = delete;
/// 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() = delete;
CPUHybridDecoderRecompiler(const CPUHybridDecoderRecompiler & other) = delete;
CPUHybridDecoderRecompiler(CPUHybridDecoderRecompiler && other) = delete;
virtual ~CPUHybridDecoderRecompiler();
CPUHybridDecoderRecompiler & operator = (const ExecutionEngine & other) = delete;
CPUHybridDecoderRecompiler & operator = (ExecutionEngine && other) = delete;
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, Executable> &executableMap;
public:
CustomSectionMemoryManager(std::unordered_map<std::string, Executable> &map) :
executableMap(map)
{}
~CustomSectionMemoryManager() override {}
virtual uint64_t getSymbolAddress(const std::string &Name) override
{
std::unordered_map<std::string, Executable>::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