mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-07 15:31: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 "Utilities/Log.h"
|
||||
#include "Emu/Cell/PPULLVMRecompiler.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#undef _
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/ExecutionEngine/GenericValue.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/CodeGen/MachineCodeInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
PPULLVMRecompiler::PPULLVMRecompiler(PPUThread & ppu)
|
||||
: m_ppu(ppu)
|
||||
, m_decoder(new PPUDecoder(this))
|
||||
, m_decoder(this)
|
||||
, m_ir_builder(m_llvm_context)
|
||||
{
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetDisassembler();
|
||||
|
||||
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");
|
||||
|
@ -24,31 +30,35 @@ PPULLVMRecompiler::PPULLVMRecompiler(PPUThread & ppu)
|
|||
m_execution_engine->addGlobalMapping(m_gpr, m_ppu.GPR);
|
||||
m_execution_engine->addGlobalMapping(m_vpr, m_ppu.VPR);
|
||||
m_execution_engine->addGlobalMapping(m_vscr, &m_ppu.VSCR);
|
||||
|
||||
m_disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr);
|
||||
|
||||
RunUnitTests();
|
||||
}
|
||||
|
||||
PPULLVMRecompiler::~PPULLVMRecompiler()
|
||||
{
|
||||
LLVMDisasmDispose(m_disassembler);
|
||||
delete m_execution_engine;
|
||||
llvm_shutdown();
|
||||
delete m_decoder;
|
||||
}
|
||||
|
||||
u8 PPULLVMRecompiler::DecodeMemory(const u64 address)
|
||||
{
|
||||
std::string function_name = fmt::Format("fn_%llx", address);
|
||||
Function * function = m_module->getFunction(function_name);
|
||||
auto function_name = fmt::Format("fn_0x%llx", address);
|
||||
auto function = m_module->getFunction(function_name);
|
||||
|
||||
if (!function)
|
||||
{
|
||||
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_hit_branch_instruction = false;
|
||||
//while (!m_hit_branch_instruction)
|
||||
//{
|
||||
// u32 instr = Memory.Read32(address);
|
||||
// m_decoder->Decode(instr);
|
||||
// m_decoder.Decode(instr);
|
||||
//}
|
||||
|
||||
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)
|
||||
{
|
||||
s32 a = m_ppu.GPR[ra];
|
||||
s32 a = (s32)m_ppu.GPR[ra];
|
||||
|
||||
if ((a < simm16 && (to & 0x10)) ||
|
||||
(a > simm16 && (to & 0x8)) ||
|
||||
|
@ -4005,7 +4015,73 @@ void PPULLVMRecompiler::SetVr(u32 vr, Value * val)
|
|||
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/GlobalVariable.h"
|
||||
#include "llvm/ExecutionEngine/JIT.h"
|
||||
#include "llvm/MC/MCDisassembler.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
/// LLVM based PPU emulator
|
||||
class PPULLVMRecompiler : public CPUDecoder, protected PPUOpcodes
|
||||
{
|
||||
public:
|
||||
|
@ -420,22 +420,56 @@ protected:
|
|||
void UNK(const u32 code, const u32 opcode, const u32 gcode) override;
|
||||
|
||||
private:
|
||||
/// PPU processor context
|
||||
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;
|
||||
|
||||
LLVMContext m_llvm_context;
|
||||
IRBuilder<> m_ir_builder;
|
||||
/// 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;
|
||||
GlobalVariable * m_gpr;
|
||||
GlobalVariable * m_vpr;
|
||||
GlobalVariable * m_vscr;
|
||||
ExecutionEngine * m_execution_engine;
|
||||
|
||||
Value * GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits);
|
||||
Value * GetVrAsFloatVec(u32 vr);
|
||||
Value * GetVrAsDoubleVec(u32 vr);
|
||||
void SetVr(u32 vr, Value * val);
|
||||
/// Global variable in m_module that corresponds to m_ppu.GPR
|
||||
llvm::GlobalVariable * m_gpr;
|
||||
|
||||
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