Implemented a basic UT framework for PPULLVMRecompiler

This commit is contained in:
S Gopal Rajagopal 2014-09-15 19:46:53 +05:30
parent 2bb63ad051
commit 2ea881301a
2 changed files with 4562 additions and 4452 deletions

View file

@ -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)) ||
@ -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);
} }

View file

@ -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);
}; };