mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-07 23:41:26 +12:00
Implemented a basic UT framework for PPULLVMRecompiler
This commit is contained in:
parent
2bb63ad051
commit
2ea881301a
2 changed files with 4562 additions and 4452 deletions
|
@ -1,19 +1,25 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
#include "Utilities/Log.h"
|
||||||
#include "Emu/Cell/PPULLVMRecompiler.h"
|
#include "Emu/Cell/PPULLVMRecompiler.h"
|
||||||
#include "Emu/Memory/Memory.h"
|
#include "Emu/Memory/Memory.h"
|
||||||
#include "llvm/Support/TargetSelect.h"
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/Support/Host.h"
|
||||||
#include "llvm/Support/ManagedStatic.h"
|
#include "llvm/Support/ManagedStatic.h"
|
||||||
#undef _
|
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include "llvm/ExecutionEngine/GenericValue.h"
|
#include "llvm/ExecutionEngine/GenericValue.h"
|
||||||
#include "llvm/IR/Intrinsics.h"
|
#include "llvm/IR/Intrinsics.h"
|
||||||
|
#include "llvm/Analysis/Verifier.h"
|
||||||
|
#include "llvm/CodeGen/MachineCodeInfo.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
PPULLVMRecompiler::PPULLVMRecompiler(PPUThread & ppu)
|
PPULLVMRecompiler::PPULLVMRecompiler(PPUThread & ppu)
|
||||||
: m_ppu(ppu)
|
: m_ppu(ppu)
|
||||||
, m_decoder(new PPUDecoder(this))
|
, m_decoder(this)
|
||||||
, m_ir_builder(m_llvm_context)
|
, m_ir_builder(m_llvm_context)
|
||||||
{
|
{
|
||||||
InitializeNativeTarget();
|
InitializeNativeTarget();
|
||||||
|
InitializeNativeTargetDisassembler();
|
||||||
|
|
||||||
m_module = new Module("Module", m_llvm_context);
|
m_module = new Module("Module", m_llvm_context);
|
||||||
m_gpr = new GlobalVariable(*m_module, ArrayType::get(Type::getInt64Ty(m_llvm_context), 32), false, GlobalValue::ExternalLinkage, nullptr, "gpr");
|
m_gpr = new GlobalVariable(*m_module, ArrayType::get(Type::getInt64Ty(m_llvm_context), 32), false, GlobalValue::ExternalLinkage, nullptr, "gpr");
|
||||||
|
@ -24,31 +30,35 @@ PPULLVMRecompiler::PPULLVMRecompiler(PPUThread & ppu)
|
||||||
m_execution_engine->addGlobalMapping(m_gpr, m_ppu.GPR);
|
m_execution_engine->addGlobalMapping(m_gpr, m_ppu.GPR);
|
||||||
m_execution_engine->addGlobalMapping(m_vpr, m_ppu.VPR);
|
m_execution_engine->addGlobalMapping(m_vpr, m_ppu.VPR);
|
||||||
m_execution_engine->addGlobalMapping(m_vscr, &m_ppu.VSCR);
|
m_execution_engine->addGlobalMapping(m_vscr, &m_ppu.VSCR);
|
||||||
|
|
||||||
|
m_disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr);
|
||||||
|
|
||||||
|
RunUnitTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
PPULLVMRecompiler::~PPULLVMRecompiler()
|
PPULLVMRecompiler::~PPULLVMRecompiler()
|
||||||
{
|
{
|
||||||
|
LLVMDisasmDispose(m_disassembler);
|
||||||
delete m_execution_engine;
|
delete m_execution_engine;
|
||||||
llvm_shutdown();
|
llvm_shutdown();
|
||||||
delete m_decoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 PPULLVMRecompiler::DecodeMemory(const u64 address)
|
u8 PPULLVMRecompiler::DecodeMemory(const u64 address)
|
||||||
{
|
{
|
||||||
std::string function_name = fmt::Format("fn_%llx", address);
|
auto function_name = fmt::Format("fn_0x%llx", address);
|
||||||
Function * function = m_module->getFunction(function_name);
|
auto function = m_module->getFunction(function_name);
|
||||||
|
|
||||||
if (!function)
|
if (!function)
|
||||||
{
|
{
|
||||||
function = cast<Function>(m_module->getOrInsertFunction(function_name, Type::getVoidTy(m_llvm_context), (Type *)nullptr));
|
function = cast<Function>(m_module->getOrInsertFunction(function_name, Type::getVoidTy(m_llvm_context), (Type *)nullptr));
|
||||||
BasicBlock * block = BasicBlock::Create(m_llvm_context, "start", function);
|
auto block = BasicBlock::Create(m_llvm_context, "start", function);
|
||||||
m_ir_builder.SetInsertPoint(block);
|
m_ir_builder.SetInsertPoint(block);
|
||||||
|
|
||||||
//m_hit_branch_instruction = false;
|
//m_hit_branch_instruction = false;
|
||||||
//while (!m_hit_branch_instruction)
|
//while (!m_hit_branch_instruction)
|
||||||
//{
|
//{
|
||||||
// u32 instr = Memory.Read32(address);
|
// u32 instr = Memory.Read32(address);
|
||||||
// m_decoder->Decode(instr);
|
// m_decoder.Decode(instr);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
VADDSWS(14, 23, 25);
|
VADDSWS(14, 23, 25);
|
||||||
|
@ -93,7 +103,7 @@ void PPULLVMRecompiler::TDI(u32 to, u32 ra, s32 simm16)
|
||||||
|
|
||||||
void PPULLVMRecompiler::TWI(u32 to, u32 ra, s32 simm16)
|
void PPULLVMRecompiler::TWI(u32 to, u32 ra, s32 simm16)
|
||||||
{
|
{
|
||||||
s32 a = m_ppu.GPR[ra];
|
s32 a = (s32)m_ppu.GPR[ra];
|
||||||
|
|
||||||
if ((a < simm16 && (to & 0x10)) ||
|
if ((a < simm16 && (to & 0x10)) ||
|
||||||
(a > simm16 && (to & 0x8)) ||
|
(a > simm16 && (to & 0x8)) ||
|
||||||
|
@ -194,7 +204,7 @@ void PPULLVMRecompiler::VADDSWS(u32 vd, u32 va, u32 vb)
|
||||||
auto tmp7_v4i32 = m_ir_builder.CreateAShr(tmp6_v4i32, 31);
|
auto tmp7_v4i32 = m_ir_builder.CreateAShr(tmp6_v4i32, 31);
|
||||||
|
|
||||||
// tmp7 is equal to 0xFFFFFFFF if an overflow occured and 0x00000000 otherwise. tmp9 is bitwise inverse of tmp7.
|
// tmp7 is equal to 0xFFFFFFFF if an overflow occured and 0x00000000 otherwise. tmp9 is bitwise inverse of tmp7.
|
||||||
u32 tmp8_v4i32[4] ={0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
u32 tmp8_v4i32[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
||||||
auto tmp9_v4i32 = m_ir_builder.CreateXor(tmp7_v4i32, ConstantDataVector::get(m_llvm_context, tmp8_v4i32));
|
auto tmp9_v4i32 = m_ir_builder.CreateXor(tmp7_v4i32, ConstantDataVector::get(m_llvm_context, tmp8_v4i32));
|
||||||
auto tmp10_v4i32 = m_ir_builder.CreateAnd(tmp3_v4i32, tmp7_v4i32);
|
auto tmp10_v4i32 = m_ir_builder.CreateAnd(tmp3_v4i32, tmp7_v4i32);
|
||||||
auto tmp11_v4i32 = m_ir_builder.CreateAnd(sum_v4i32, tmp9_v4i32);
|
auto tmp11_v4i32 = m_ir_builder.CreateAnd(sum_v4i32, tmp9_v4i32);
|
||||||
|
@ -4005,7 +4015,73 @@ void PPULLVMRecompiler::SetVr(u32 vr, Value * val)
|
||||||
m_ir_builder.CreateStore(val_i128, vr_i128_ptr);
|
m_ir_builder.CreateStore(val_i128, vr_i128_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPULLVMRecompiler::RunTests()
|
void PPULLVMRecompiler::RunUnitTests()
|
||||||
{
|
{
|
||||||
|
LOG_NOTICE(PPU, "Running Unit Tests");
|
||||||
|
|
||||||
|
RunUnitTest("MFVSCR",
|
||||||
|
[this] ()
|
||||||
|
{
|
||||||
|
MFVSCR(1);
|
||||||
|
},
|
||||||
|
[this] ()
|
||||||
|
{
|
||||||
|
m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[0] = m_ppu.VPR[1]._u32[0] = 0x9ABCDEF0;
|
||||||
|
m_ppu.VSCR.VSCR = 0x12345678;
|
||||||
|
},
|
||||||
|
[this](std::string & msg)
|
||||||
|
{
|
||||||
|
msg = fmt::Format("VPR[1] = %s", m_ppu.VPR[1].ToString(true).c_str());
|
||||||
|
return m_ppu.VPR[1].Equals(0x12345678, 0, 0, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPULLVMRecompiler::RunUnitTest(const char * name, std::function<void()> test_case, std::function<void()> input, std::function<bool(std::string & msg)> check_result)
|
||||||
|
{
|
||||||
|
// Create the unit test function
|
||||||
|
auto function = cast<Function>(m_module->getOrInsertFunction(name, Type::getVoidTy(m_llvm_context), (Type *)nullptr));
|
||||||
|
auto block = BasicBlock::Create(m_llvm_context, "start", function);
|
||||||
|
m_ir_builder.SetInsertPoint(block);
|
||||||
|
test_case();
|
||||||
|
m_ir_builder.CreateRetVoid();
|
||||||
|
verifyFunction(*function);
|
||||||
|
|
||||||
|
// Print the IR
|
||||||
|
std::string ir;
|
||||||
|
raw_string_ostream ir_ostream(ir);
|
||||||
|
function->print(ir_ostream);
|
||||||
|
LOG_NOTICE(PPU, "[UT %s] LLVM IR:%s", name, ir.c_str());
|
||||||
|
|
||||||
|
// Generate the function
|
||||||
|
MachineCodeInfo mci;
|
||||||
|
m_execution_engine->runJITOnFunction(function, &mci);
|
||||||
|
|
||||||
|
// Disassember the generated function
|
||||||
|
LOG_NOTICE(PPU, "[UT %s] Disassembly:", name);
|
||||||
|
for (uint64_t pc = 0; pc < mci.size();)
|
||||||
|
{
|
||||||
|
char str[1024];
|
||||||
|
|
||||||
|
pc += LLVMDisasmInstruction(m_disassembler, (uint8_t *)mci.address() + pc, mci.size() - pc, (uint64_t)((uint8_t *)mci.address() + pc), str, sizeof(str));
|
||||||
|
LOG_NOTICE(PPU, "[UT %s] %s.", name, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the test
|
||||||
|
input();
|
||||||
|
std::vector<GenericValue> args;
|
||||||
|
m_execution_engine->runFunction(function, args);
|
||||||
|
|
||||||
|
// Verify results
|
||||||
|
std::string msg;
|
||||||
|
bool pass = check_result(msg);
|
||||||
|
if (pass)
|
||||||
|
{
|
||||||
|
LOG_NOTICE(PPU, "[UT %s] Test passed. %s.", name, msg.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR(PPU, "[UT %s] Test failed. %s.", name, msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_execution_engine->freeMachineCodeForFunction(function);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/IR/GlobalVariable.h"
|
#include "llvm/IR/GlobalVariable.h"
|
||||||
#include "llvm/ExecutionEngine/JIT.h"
|
#include "llvm/ExecutionEngine/JIT.h"
|
||||||
|
#include "llvm/MC/MCDisassembler.h"
|
||||||
|
|
||||||
using namespace llvm;
|
/// LLVM based PPU emulator
|
||||||
|
|
||||||
class PPULLVMRecompiler : public CPUDecoder, protected PPUOpcodes
|
class PPULLVMRecompiler : public CPUDecoder, protected PPUOpcodes
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -420,22 +420,56 @@ protected:
|
||||||
void UNK(const u32 code, const u32 opcode, const u32 gcode) override;
|
void UNK(const u32 code, const u32 opcode, const u32 gcode) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// PPU processor context
|
||||||
PPUThread & m_ppu;
|
PPUThread & m_ppu;
|
||||||
PPUDecoder * m_decoder;
|
|
||||||
|
/// PPU instruction decoder
|
||||||
|
PPUDecoder m_decoder;
|
||||||
|
|
||||||
|
/// A flag used to detect branch instructions.
|
||||||
|
/// This is set to false at the start of compilation of a block.
|
||||||
|
/// When a branch instruction is encountered, this is set to true by the decode function.
|
||||||
bool m_hit_branch_instruction;
|
bool m_hit_branch_instruction;
|
||||||
|
|
||||||
LLVMContext m_llvm_context;
|
/// LLVM context
|
||||||
IRBuilder<> m_ir_builder;
|
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::Module * m_module;
|
||||||
GlobalVariable * m_gpr;
|
|
||||||
GlobalVariable * m_vpr;
|
|
||||||
GlobalVariable * m_vscr;
|
|
||||||
ExecutionEngine * m_execution_engine;
|
|
||||||
|
|
||||||
Value * GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits);
|
/// Global variable in m_module that corresponds to m_ppu.GPR
|
||||||
Value * GetVrAsFloatVec(u32 vr);
|
llvm::GlobalVariable * m_gpr;
|
||||||
Value * GetVrAsDoubleVec(u32 vr);
|
|
||||||
void SetVr(u32 vr, Value * val);
|
|
||||||
|
|
||||||
void RunTests();
|
/// Global variable in m_module that corresponds to m_ppu.VPR
|
||||||
|
llvm::GlobalVariable * m_vpr;
|
||||||
|
|
||||||
|
/// Global variable in m_module that corresponds to m_ppu.VSCR
|
||||||
|
llvm::GlobalVariable * m_vscr;
|
||||||
|
|
||||||
|
/// JIT execution engine
|
||||||
|
llvm::ExecutionEngine * m_execution_engine;
|
||||||
|
|
||||||
|
/// Disassembler
|
||||||
|
LLVMDisasmContextRef m_disassembler;
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// Execute all unit tests
|
||||||
|
void RunUnitTests();
|
||||||
|
|
||||||
|
/// Excute an unit test
|
||||||
|
void RunUnitTest(const char * name, std::function<void()> test_case, std::function<void()> input, std::function<bool(std::string & msg)> check_result);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue